盒子
盒子
文章目录
  1. 开源框架面试篇
    1. 1. 简单讲讲tomcat结构,以及其类加载流程,线程模型
    2. 2. tomcat如何调优,涉及哪些参数
    3. 3. 讲讲Spring加载流程
    4. 4. Spring AOP的实现原理, AOP中的几个术语,它们是怎么相互工作的
    5. 5. 讲讲Spring事务的传播属性
    6. 6. Spring如何管理事务的
    7. 7. Spring怎么配置事务(具体说出一些关键的xml元素)
    8. 8. 说说你对Spring的的理解,非单例注入的原理?它的生命周期?循环注入的原理?
    9. 9. SpringMVC中DispatcherServlet初始化过程
    10. 10. Netty的线程模型, netty如何基于reactor模型上实现的
    11. 11. 为什么选择netty
    12. 12. 谈谈TCP三次握手,四次挥手
    13. 13. 什么是TCP粘包、拆包,解决方式是什么
    14. 14. Netty的fashwheeltimer的用法,实现原理,是否出现过调用不够准时,怎么解决
    15. 15. Netty的心跳处理在弱网下怎么办
    16. 16. Netty的通许协议是什么样的
    17. 17. SpringMVC用到的注解,作用是什么,原理
    18. 18. SpringBoot启动机制
    19. 19. SpringBoot与Spring区别

开源框架面试篇

开源框架面试篇

1. 简单讲讲tomcat结构,以及其类加载流程,线程模型

结构

  • 模块组成结构

Tomcat和核心组件是Connector和Container,一个Connector和一个Container(Engine)构成了一个Service.Service是对外提供服务的组件,有了Service组件Tomcat就能对外提供服务了,但是光有服务还不行,还需要最外层的Server环境让Service提供服务才行。
[追问时可回答]Collector是一个连接器,主要负责接收请求并把请求交给Container;Container是一个容器,主要装载的是处理请求的组件;Service主要为了关联Collector和Container,只有这两个结合起来才能够处理请求;Server对外提供接口访问Service,对内维护Service集合包括管理Service生命周期等

  • 文档组成结构[可不回答]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- Server代表整个容器,是Tomcat的顶层元素。服务器默认在8005端口,shutdown命令=关闭Tomcat -->
<Server>
<Listener />
<GlobaNamingResources></GlobaNamingResources>
<!-- Service包含多个Connector元素,而这些Connector元素共享一个Engine元素。 -->
<Service>
<!-- Connector元素代表与客户时间交互的组件,它负责接收客户的请求,以及向客户响应结果,配置http为https主要是修改Connector -->
<Connector />
<!-- 每个Service只能有一个Engine元素,处理同一个Service中所有Connector元素接收到的客户请求.Engine用来处理Connetcor收到的Http请求它匹配请求和自己的虚拟主机,并把请求转给对应的Host来处理 -->
<Engine>
<Logger />
<Realm />
<!-- 一个Engine包含多个host元素,每个host元素定义了一个虚拟主机,它包含一个或多个Web应用 -->
<host>
<Logger />
<!-- 由Context接口定义.是使用最频繁的元素,对应于一个Web App -->
<Context />
</host>
</Engine>
</Service>
</Server>

类加载流程

Tomcat的类加载机制是违背双亲委托机制的,对于一些未加载的非基础类(Object、String)等,各个web应用自己的类加载器(WebAppClassLoader)加载,加载不到时在交给CommonClassLoader走双亲委托

当应用需要一个类时,则会按照以下顺序进行类加载:
1) 使用boostrap引导类加载器加载
2) 使用system系统类加载器加载
3) 使用应用类加载器在WEB-INF/classes中加载
4) 使用应用类加载器在WEB-INF/lib中加载
5) 使用common类加载器在CATALINA_HOME/lib中加载

线程模型

支持以下四种线程模型

模型 描述
BIO 同步阻塞,每个请求都会创建一个线程
NIO 同步非阻塞,比传统的BIO能更好的支持大并发,tomcat8.0后默认采用该模式
APR tomcat以JNI形式调用http服务器的核心动态链接库来处理读取与网络传输操作,需要编译安装APR库
AIO 异步非阻塞,tomcat8.0后支持

2. tomcat如何调优,涉及哪些参数

tomcat调优主要从四个方面考虑1)吞吐量 2)响应时间 3)CPU负载 4)内存利用率

1)Tomcat启动参数调优

Tomcat启动参数位于tomcat安装目录\bin目录下,linux是catalina.sh文件,windows是catalina.bat文件

  • 设置-Xms(最小堆内存) -Xmx(最大堆内存)一致,避免当Xms向Xmx变化时,CPU高速运转出发垃圾回收机制,严重时导致系统卡壳
    [tip]在设置Xmx时先执行java -Xms 1500m -version,如果能正常显示版本信息,则证明可用
  • -Xmn: 设置年轻代大小为512m,整个堆大小=年轻代+老年代+持久代。持久代一般大小为64m,所以增大年轻代后,老年代将减少。官方推荐设置整个堆的3/8
  • -Xss: 设置每个线程的堆栈大小,一般不宜超过1M

2)Tomcat容器内调优**

打开[tomcat安装目录]\conf\server.xml,更改后的配置如下

1
2
3
4
5
6
7
8
<Connector port="8080" protocol="HTTP/1.1"           
URIEncoding="UTF-8"  minSpareThreads="25" maxSpareThreads="75"         
enableLookups="false" disableUploadTimeout="true" connectionTimeout="20000" 
acceptCount="300"  maxThreads="300" maxProcessors="1000" minProcessors="5"
useURIValidationHack="false"
compression="on" compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" 
redirectPort="8443" />

-URIEncoding=”UTF-8”: 使得url可以解析带有中文的url
-minSpareThreads: 最小备用线程数,tomcat启动时初始化的线程数
-maxSpareThreads: 如果空闲的线程数大于该设置数,则终止多余的空闲线程,减少池中线程总数
-enableLookups: 设置为false,关闭DNS查询,从而消除DNS查询对性能的影响
-disableUploadTimeout: 允许servlet容器,正在执行使用一个较长的连接超时值,以使Servlet有较长的时间来执行完成,默认为false
-connectionTimeout: 网络连接超时时间毫秒数,默认60s
-acceptCount: 当线程数到达maxThreads后,后续请求会放入等待队列,acceptCount即为等待队列的大小
-maxThreads: 可创建的最大线程数,即最大并发数
-maxProcessors与minProcessors: 在 Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。 通常Windows是1000个左右,Linux是2000个左右。???
-useURIValidationHack: 禁用url检查
-compression: 打开压缩功能
-compressionMinSize: 启用压缩的输出内容大小,默认为2K
-noCompressionUserAgents=”gozilla, traviata”: 设置不启用压缩的浏览器
-compressableMimeType: 压缩类型
-redirectPort: 如果走https协议的话,则会用到

3. 讲讲Spring加载流程

初始化环境 -> 加载配置文件 -> 实例化bean -> 调用Bean显示信息
// TODO 详细重点说明

4. Spring AOP的实现原理, AOP中的几个术语,它们是怎么相互工作的

两种主要实现方式:JDK动态代理与CGLIB
在Spring5中DefaultAopProxyFactory中调用createAopProxy(AdvisedSupport config)创建AopProxy
若AdvisedSupport中设置optimize=true并且proxyTargetClass=true并且目标targetClass不是interface和proxy,则采用ObjenesisCglibAopProxy创建AopProxy,否则,采用JdkDynamicAopProxy创建AopProxy

JdkDynamicAopProxy实现InvocationHandler接口的方法invoke(Object proxy, Method method, Object[] args)得到AopProxy,
其中较为重要的是通过AdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> tagetClass)获取拦截器链,该方法在DefaultAdvisorChainFactory中实现,其中,主要通过DefaultAdvisorAdapterRegistry.getInterceptors(Advisor advisor)获取拦截器,这样就和advisor连接起来了。最后通过getInterceptors中的AdvisorAdpter获取MethodBeforeInterceptor、AfterReturningInterceptor、ThrowsAdviceInterceptor,
如在AfterReturningInterceptor中,其实现了MethodInterceptor与AfterAdvice, 从而在invoke(MethodInvocation mi)中先调用mi.process(), 然后调用advice.afterReturning就完成了后置增强

ObjenesisCglibAopProxy通过createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks)创建AopProxy, enhancer.createClass()创建proxyClass, proxyClass.getDeclaredConstructor()创建Constructor, 最后constructor.newInstance()获取proxyInstance()

主要术语

advice通知 拦截增强
pointcut切入点 目标类
advisor 通过其子类PointcutAdvisor将Advice与PointCut连接起来

5. 讲讲Spring事务的传播属性

  • 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也要回滚。

  • 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事务仍然可能提交

  • MANDATORY: 支持当前事务,如果当前没有事务,则抛出异常

  • SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行
  • NOT_SUPPORTED: 以非事务方式执行,如果当前存在事务,则挂起
  • NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常

6. Spring如何管理事务的

Spring事务管理主要包括三个接口:
1) PlatformTransactionManager: 事务管理器,主要包括commit()、rollback()、getTransaction()
2) TransactionDefinition: 事务定义信息。主要包括getIsolationLevel()、getPropagationBehavior()、getTimeout()、isReadOnly()
3) TransactionStatus: 事务具体运行状态。主要包括hasSavepoint()、isCompleted()、isNewTransaction()

7. Spring怎么配置事务(具体说出一些关键的xml元素)

配置事务的方式有两种:
1)基于XML的事务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8"?>
<!-- from the file 'context.xml' -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<!-- 数据元信息 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>

<!-- 管理事务的类,指定我们用谁来管理我们的事务-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- 首先我们要把服务对象声明成一个bean 例如HelloService -->
<bean id="helloService" class="com.yintong.service.HelloService"/>

<!-- 然后是声明一个事物建议tx:advice,spring为我们提供了事物的封装,这个就是封装在了<tx:advice/>中 -->
<!-- <tx:advice/>有一个transaction-manager属性,我们可以用它来指定我们的事物由谁来管理。
默认:事务传播设置是 REQUIRED,隔离级别是DEFAULT -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 配置这个事务建议的属性 -->
<tx:attributes>
<!-- 指定所有get开头的方法执行在只读事务上下文中 -->
<tx:method name="get*" read-only="true"/>
<!-- 其余方法执行在默认的读写上下文中 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<!-- 我们定义一个切面,它匹配FooService接口定义的所有操作 -->
<aop:config>
<!-- <aop:pointcut/>元素定义AspectJ的切面表示法,这里是表示com.yintong.service.helloService包下的任意方法。 -->
<aop:pointcut id="helloServiceOperation" expression="execution(* com.yintong.service.helloService.*(..))"/>
<!-- 然后我们用一个通知器:<aop:advisor/>把这个切面和tx:advice绑定在一起,表示当这个切面:fooServiceOperation执行时tx:advice定义的通知逻辑将被执行 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="helloServiceOperation"/>
</aop:config>

</beans>

2)基于注解的事务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<bean id="helloService" class="com.yintong.service.HelloService"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置注解事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>

8. 说说你对Spring的的理解,非单例注入的原理?它的生命周期?循环注入的原理?

谈谈对Spring的理解
1) Srping是一个开源框架,主要为简化企业级应用开发而生

  • 控制反转(IOC): Spring容器使用了工厂模式为我们创建了所需的对象,我们使用时不需要自己创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想
  • 依赖注入(DI): Spring使用Java Bean的Set方法或者带参数的构造方法为我们创建所需对象时将其属性自动设置所需值得过程就是依赖注入的思想
  • 面向切面编程(AOP): 在面向切面编程中,我们将一个个对象某些类似方面抽象成一个切面,对这个切面进行权限验证、事务管理、记录日志等公共操作
    2) 在Spring中所有管理的都是JavaBean对象,而BeanFactory和ApplicationContext就是Spring框架的IOC容器,一般使用ApplicationContext,不仅包含BeanFacotry,同时还行了很多扩展
    非单例注入的原理

问题描述:singleton类型的bean A方法,需要调用prototype类型的bean B方法,对于bean A来说,容器只会调用一次,这样就没法在需要的时候每次让容器为bean A提供一个新的bean B实例
解决方案:
1) 放弃控制反转: 通过实现ApplicationContextAware接口

1
2
3
4
5
6
7
8
9
10
public class CommandManager implements ApplicationContextAware {   
private ApplicationContext applicationContext;
public Command createCommand() {
return (Command) this.applicationContext.getBean("asyncCommand");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

2) 采用Lookup方法注入

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 通过scope="prototype"界定该bean是多例的 -->
<bean id="asyncCommand" class="learn.frame.spring.scope.dropioc.AsyncCommand"
scope="prototype"></bean>

<bean id="commandManager" class="learn.frame.spring.scope.lookup.CommandManager">
<lookup-method name="createCommand" bean="asyncCommand"/>
</bean>
</beans>

[辅助理解]

  1. 被注入方法不一定是抽象的,如果被注入方法是抽象的,动态生成的子类(这里就是动态生成的CommandManager的子类)会实现该方法。否则,动态生成的子类会覆盖类里的具体方法
  2. 为了让这个动态子类得以正常工作,需要把CGLIB的jar文件放在classpath里,这就是我们引用cglib包的原因
  3. Spring容器要子类化的类(CommandManager)不能是final的,要覆盖的方法(createCommand)也不能是final的

Spring生命周期
Spring Bean生命周期

9. SpringMVC中DispatcherServlet初始化过程

10. Netty的线程模型, netty如何基于reactor模型上实现的

11. 为什么选择netty

12. 谈谈TCP三次握手,四次挥手

13. 什么是TCP粘包、拆包,解决方式是什么

TCP是基于字节流的,且首部没有表示数据长度的字段,因此才有粘包、拆包现象发生。
具体表现形式为:客户端向服务端发送包packet1与packet2,如果服务端接收到了一个包,由于TCP是不会丢包的,则发生了粘包;
如果服务器接收到了两个包,其中一个多出来一块,一个少了一块,则发生了粘包与拆包

粘包、拆包原因[辅助理解,可不回答]

  1. 要发送数据大于TCP发送缓冲区剩余空间大小,则发生拆包
  2. 待发送的数据大于MSS(最大报文长度),则在传输前进行拆包
  3. 要发送的数据小于TCP发送缓冲区剩余空间大小,TCP将多次写入的数据一次发送出去,则发生粘包
  4. 接收数据端的应用层没有及时接收缓冲区中的数据,将发生粘包

解决方法

  1. 发送端给每个数据包添加包首部,首部中至少包含数据包的长度
  2. 发送端将数据端封装 成固定长度(不够的可以通过补0填充)
  3. 可以在数据包之间设置边界,如添加特殊符号

扩展:UDP是基于报文发送的,首部采用16bit来指示UDP数据报文长度,因此不会发生粘包、拆包

14. Netty的fashwheeltimer的用法,实现原理,是否出现过调用不够准时,怎么解决

15. Netty的心跳处理在弱网下怎么办

16. Netty的通许协议是什么样的

17. SpringMVC用到的注解,作用是什么,原理

SpringMVC运行原理
1) Http请求: 客户端请求提交到DispatcherServlet
2) 寻找处理器: 由DispatcherServlet查询一个或多个HandlerMapping,找到处理请求的Controller
3) 调用处理器: DispatcherServlet将请求提交到Controller
4) 调用业务处理和返回结果: Controller调用业务处理后,返回ModelAndView
5) 处理视图映射并返回模型: DispatcherServlet查询一个或多个ViewResolver,找到ModelAndView指定的视图
6) Http响应: 视图将结果显示到客户端

主要注解
@Controller 用于注解控制层
@RestController: 相当于@Controller与@ResponseBody
@Component 泛指组件
@Repository 用于注解dao层
@Service 用于注解业务层

@RequestBody @RequestMapping @Autowired @PathVariable @RequestParam @RequestHeader等

18. SpringBoot启动机制

19. SpringBoot与Spring区别

SpringBoot实现了自动配置,降低了项目搭建的复杂度
SpringBoot集成了大量常用第三方库,几乎零配置或少配置开箱即用

支持一下
扫一扫,支持沈健
  • 微信扫一扫
  • 支付宝扫一扫