Java面试——开源框架知识

2023-09-25 15:11:25 浏览数 (2)

一、简单讲讲 Tomcat结构,以及其类加载器流程,线程模型等

【1】模块组成结构:Tomcat 的核心组件就 Connector 和 Container,一个Connector 一个Container(Engine)构成一个Service,Service就是对外提供服务的组件,有了Service组件Tomcat就能对外提供服务了,但是光有服务还不行,还需要有环境让你提供服务才行,所以最外层的Server就是为Service提供了生存的土壤。

▁▂▃ Connector是一个连接器,主要负责接受请求并把请求交给Container,Container就是一个容器,主要装的是具有处理请求的组件。Service主要是为了关联 Container与 Connect,只有两个结合起来才能够处理一个请求。Server负责管理 Service集合,从图中我们可以看到Tomcat可以提供多种服务,那么这些Service就是由Server来管理的。具体工作包括:对外提供一个接口访问Service,对内维护 Service集合,维护 Service集合包括管理 Service声明周期等等。 【2】文档结构组成:

代码语言:javascript复制
<!-- 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>

【3】类加载器流程:Tomcat启动时,会创建以下 4种类加载器: 1)、Bootstrap 引导类加载器:加载JVM启动所需的类,以及标准扩展类(位于jar/lib/ext上) 2)、System 系统类加载器:加载Tomcat启动时的类,比如bootstrap.jar通常在catalina.bat或者catalina.sh中指定。指定位置位于CATALINA_HOME/bin下。

3)、Common 通用类加载器:加载 tomcat使用以及应用通用的一些类,位于 CATALINA_HOME/lib下,比如servlet-api.jar

4)、webapp应用类加载器:每个应用在创建后,都会创建一个唯一的类加载器。该类加载器会加载位于 WEB-INF/lib下的 jar文件中的 class和 WEB-INF/classes下的 class文件。

红色虚线表示】:应用需要到某个类时,则会按照下面的顺序进行类加载。 ①、使用 bootstrap引导类加载器加载 ②、使用 system系统类加载器加载 ③、使用应用类加载器在 WEB-INF/classes中加载 ④、使用应用类加载器在 WEB-INF/lib中加载 ⑤、使用 common类加载器在 CATALINA_HOME/lib中加载

【4】线程模型:支持以下四种线程模型。

主要介绍,NIO线程模型】:Poller组件在非阻塞的模式下轮询多个客户端连接,不断检测,处理各种事件。例如检测各个连接是否可读,对于可读的客户端连接尝试进行读取和解析消息报文。

NioEndpoint 组件包含很多子组件:LimitLatch(连接数控制器)Acceptor(套接字接收器)Poller(轮询器)Poller池SocketProcessor(任务处理器)以及Executor(任务执行器)。 ✘ LimitLatch(连接数控制器):负责对连接数的控制。初始化同步器的最大限制值,然后每接受一个套接字就将计数器变量 1,每关闭一个套接字,将计数器变量-1,如此一来,一旦技术变量值>最大限制值,则AQS机制将接受线程阻塞,而停止对套接字的接受。直到某些套接字处理完,关闭后重新唤起接受线程往下接受套接字。 Tomcat默认同时接受客户端连接数为200,但可以通过server.xml中的节点的maxConnections属性进行调节,Tomcat BIO模式下的LimitLatch的限制数与线程池的最大线程数密切相关。他们之间的比例1:1。 ✘ Acceptor(套接字接收器):负责接收套接字连接并注册到通道连接里面。(接收请求) ✘ Poller(轮询器):负责轮询检查事件列表。 ✘ Poller池:包含了若干个轮询组件。 ✘ SocketProcessor(任务处理器):任务定义器,将socket扔进线程池前需要定义好任务。主要任务有3个任务:处理套接字并响应客户端,连接数计数器减1,关闭套接字。套接字的处理包括对底层套接字字节流的处理,HTTP协议请求报文的报文的解析(请求头,请求体,请求行等信息的解析),根据请求行解析得到的路径去寻找相应虚拟主机上的Web项目资源,根据处理好的结果组装好HTTP协议响应报文输出到客户端。 ✘ Executor(任务执行器):负责处理套接字的线程池。 整体的流程图如下:

二、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文件中添加如下参数(重要参数随后说明):

代码语言:javascript复制
export JAVA_OPTS="-server -Xms1400M -Xmx1400M -Xss512k -XX: AggressiveOpts -XX: UseBiasedLocking 
-XX:PermSize=128M -XX:MaxPermSize=256M -XX: DisableExplicitGC -XX:MaxTenuringThreshold=31
-XX: UseConcMarkSweepGC -XX: UseParNewGC  -XX: CMSParallelRemarkEnabled 
-XX: UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m  -XX: UseFastAccessorMethods
-XX: UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true "

✔ Windowns系统中catalina.bat文件中添加如下参数(重要参数随后说明):

代码语言:javascript复制
set JAVA_OPTS=-server -Xms1400M -Xmx1400M -Xss512k -XX: AggressiveOpts -XX: UseBiasedLocking 
-XX:PermSize=128M -XX:MaxPermSize=256M -XX: DisableExplicitGC -XX:MaxTenuringThreshold=31 
-XX: UseConcMarkSweepGC -XX: UseParNewGC  -XX: CMSParallelRemarkEnabled 
-XX: UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m  -XX: UseFastAccessorMethods 
-XX: UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true

◀ -Server(重要):只要Tomcat是运行在生产环境中,这个参数必须添加。因为Tomcat默认是java-client模式运行,添加server后表示以真实的production的模式运行,将拥有更大、更高的并发处理能力,更快、更强的JVM垃圾回收机制,可以获得更多的负载和吞吐量等等。 ◀ -Xms -Xmx:既JVM内存设置了,把Xms与Xmx两个值设成一样是最优的做法。(否则当内存=Xmx向Xms变化时,CPU高速运转触发垃圾回收机制,严重时会导致系统‘卡壳’,因此一开始我们就把这两个设成一样,使得Tomcat在启动时就为最大化参数充分利用系统的效率。) ※在设这个最大内存即Xmx值时请先打开一个命令行:能够正常显示JDK的版本信息,说明这个值能够用。

◀ -Xmn:设置年轻代大小为512m。整个堆大小=年轻代 老年代 持久代。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。 ◀ -Xss:是指设定每个线程的堆栈大小。这个就要依据程序,看一个线程大约需要占用多少内存,可能会有多少线程同时运行等。一般不易设置超过1M,要不然容易出现out ofmemory。 【3】Tomcat容器内优化:打开 tomcat安装目录 confserver.xml文件。其中如下参数的默认值远远不够我们使用,我们对其进行了更改,更改后的配置如下:

代码语言:javascript复制
<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”:使得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这个类中:

代码语言:javascript复制
public Object getProxy(ClassLoader classLoader) {
   
   if (logger.isDebugEnabled()) {
   
	   logger.debug("Creating JDK dynamic proxy: target source is "  this.advised.getTargetSource());
   }
   //获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false)
   Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);
   //检查上面得到的接口中有没有定义 equals或者hashcode的接口
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   //调用Proxy.newProxyInstance创建代理对象
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

切面植入:我们都知道 InvocationHandler是 JDK动态代理的核心,生成代理对象的方法,都会委托到 InvocationHandler.invoke()方法。而通过 JdkDynamicAopProxy

代码语言:javascript复制
//获取可以应用到此方法上的Interceptor列表 
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);
//如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)
if (chain.isEmpty()) {
    
	retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args); 
} else {
   
	//创建MethodInvocation 
	invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
	retVal = invocation.proceed();             
}  

五、讲讲 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的事务配置】:

代码语言:javascript复制
<?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>  

【基于注解方式的事务配置】 : @Transactional:直接在Java源代码中声明事务的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。 ♣ 注解事务,只需要在 XML配置中配置一句就可以了,如下:

代码语言:javascript复制
<?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>

♣ 主要在类中定义事务注解 @Transactional,如下:

代码语言:javascript复制
//@Transactional 注解可以声明在类上,也可以声明在方法上。在大多数情况下,方法上的事务会首先执行
@Transactional(readOnly = true)    
public class HelloService{
     
    public Foo getFoo(String fooName) {
     
    }  
    //@Transactional 注解的事务设置将优先于类级别注解的事务设置 propagation:可选的传播性设置 
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)      
    public void updateFoo(Hel hel) {
   
    }
}  

八、说说你对 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 启动机制


十四、Netty 的心跳处理在弱网下怎么办


【参考博客】:链接

十五、Netty 的通讯协议是什么样的


【参考博客】:链接

十六、SpringMVC 用到的注解,作用是什么,原理


【参考博客】:链接

十七、SpringBoot 启动机制


【参考博客】:链接

0 人点赞