Spring面试题随记


Spring 是如何创建一个bean对象的

Spring面试题随记_第1张图片

什么是单例池,作用是什么

Spring面试题随记_第2张图片

单例池也就是这个ConcurrentHashmap,用来保存单例的bean对象,如果是多例的bean对象,那么创建bean的步骤中,就不会有下面的放入map这一步

Spring面试题随记_第3张图片

PostConstruct注解是如何工作的

Spring面试题随记_第4张图片

Spring面试题随记_第5张图片

 我们现在的需求是,想让UserService中的User属性,能在UserService初始化前被赋值,且赋的值是从数据库中读取到的

Spring面试题随记_第6张图片

实现原理,就是在初始化UserService对象的过程中,遍历UserService中所有带有PostConstruct注解的方法,然后通过反射的方式,来调用这些方法

Spring面试题随记_第7张图片

Bean的初始化是如何做的

Spring面试题随记_第8张图片

Spring面试题随记_第9张图片

上面的给user属性赋值的需求,也可以通过实现innitianlalizBean接口来实现

Bean的实例化:通过无参构造方法得到一个对象

Bean的初始化:就是调用上面构造出来的对象中的某个方法(afterProperteirSet方法) 

初始化后

Spring面试题随记_第10张图片

Spring面试题随记_第11张图片

最后,就把代理对象,放入单例池中,这样我们在使用时,直接用代理对象,就能用到用户定义的aop的逻辑

推断构造方法

Spring面试题随记_第12张图片

Spring面试题随记_第13张图片

Spring 在调用构造方法实例化对象时,会先判断调用哪个构造方法,在程序员没有显示写构造方法时,也默认调用无参构造方法,当程序员显示写有参构造方法时, 但没写无参构造方法时,无参构造方法就没有了

我们可以给构造方法加上面的注解,来明确的告诉spring 调用哪个构造方法

什么是推断构造方法中的先byType再byName

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

Spring面试题随记_第14张图片

 Spring面试题随记_第15张图片

单例bean,是指在spring 容器中,同一个beanName只对应一个bean实例,不代表说,一个类只有一个实例,可能一个类有多个实例在容器中,分别叫不同的beanName

Spring面试题随记_第16张图片

 依赖注入时,先判断容器中是否已经有OrderService类型实例,如果没有则先创建再注入给UserService,如果有,则先通过byType的方式去匹配,同一个Type有多个实例,则再通过byName的方式匹配,如果再通过name的方式匹配时,匹配不到相同的name也会报错

spring aop是怎么工作的

Spring面试题随记_第17张图片

Spring面试题随记_第18张图片

注意到这里,代理对象从父类继承过来的orderService属性是没有值的,因为spring 只有针对普通对象的依赖注入逻辑,并没有针对代理对象的依赖注入逻辑

所以,如果这里直接用super.test 调用test方法,而test方法中依赖了还为空的orderService属性,所以肯定会报错

Spring面试题随记_第19张图片

上面直接用super调用父类的test方法,是不行的

Spring面试题随记_第20张图片

 必须,在代理类中整一个target引用,把已经完成依赖注入的普通对象引用过来,然后调用普通对象的test方法

如果直接用super调用父类中的test方法

Spring 事务是怎么工作的

Spring 事务是通过aop来实现的,

Spring面试题随记_第21张图片

Configuration注解不加,事务是无法使用的 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

 上面就往spring 容器中注入了一个事务管理器

Spring 事务,是通过spring 自己的事务管理器来建立连接,然后再这同一个连接中开始一个事务,在同一个事务中执行多条sql,这多条sql中有一条报错,因为在同一个事务中,所以才能执行回滚。

Spring面试题随记_第22张图片

如果像上面这样,jdbcTemplate.execute时才建立连接,那多条sql就有多个事务,当然也就无法达到事务真正的目的了

Spring面试题随记_第23张图片

Spring 事务失效的根本原因

Spring 事务,就是给加了Transactional注解的方法所在的对象,对这个对象生成一个代理对象

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

上面这种情况,UserService的代理对象所引用的UserService普通对象,在直接调用a方法, 当然a方法上的事务注解,不会生效

Spring面试题随记_第24张图片

自己注入自己,也能使事务生效 

总结:

思考某个方法上的Transactional注解,会不会生效,就看当前是普通对象还是代理对象在调用这个方法。只有调用代理对象的方法时,事务才能生效

Configuration注解的作用是什么

Spring面试题随记_第25张图片

不加configuration注解时,事务好像就失效了一样,

所以,spring 的事务,多个线程中的sql就没办法公用同一个事务,就是因为拿不到同一个连接,因为连接是存在ThreadLocal当中的。

Spring面试题随记_第26张图片

因为,spring 要考虑,一个线程中调用多个方法,不同方法调用的是不同的数据源,也就是为了支持多数据源,所以ThreadLocal的存储格式是上面这样。

此时,jdbcTemplate执行sql是,就需要拿到连接,拿事务管理器中存放的连接,怎么拿,就通过上面的ThreadLocal去自身的线程对象中拿,

首先会拿到一个Map,然后jdbcTemplate就以和自己绑定的dataSource为key去Map中拿连接。

JdbcTemplate需要一个dataSource,事务管理器也需要一个dataSource,站在java的角度,当没有加configuration注解时,上面就是两次方法调用,两次方法调用就会返回两个dataSource对象

因为,没加configuration注解,所以有两个dataSource,所以上面jdbcTemplate以自己的dataSource为kex去Map中拿连接,肯定拿不到事务管理器中的对应连接,所以它会自己另外创建一个连接,然后这个连接不是事务管理器管理的连接,autoCommit默认就不是false(此时,每天sql执行完,立马就会提交),从而就导致了spring事务的失效。

加了configuration注解后,AppConfig对象就变成了代理对象,代理对象中的代理对象逻辑是,先判断dateSource()是否已经被调用过,如果自己调用过,那么ioc容器中就有了一个数据源对象,那么另外的地方再调用dataSource()方法时,就不会再重新创建数据源对象,而是直接去容器中取出已经创建好的数据源对象返回,

Spring面试题随记_第27张图片

Spring面试题随记_第28张图片

上面这种情况,当项目中配置了多数据源,并且加了configuration注解,但是事务仍然会失效,因为事务管理器和jdbcTemplate引用的不是同一个数据源

@Bean注解和@Conponent注解

相同点:都可以给ioc容器注册bean对象

不同点:@Bean需要与@Configuration配合,保证bean对象的唯一性。 @Bean更灵活,比如第三方类库,如果需要注册到ioc容器中,就没办法使用Conponent 注解,只能使用@Bean注解

Spring 用三级缓存来解决循环依赖问题

Spring面试题随记_第29张图片

本身加一个zhouyuMap,就能解决普通的循环依赖问题,如上图

    但是,上面在有aop的情况下,就会有问题

Spring面试题随记_第30张图片

有aop时,放入b对象a属性的是a的普通对象,但是,最终放入单例池的是a的代理对象 

针对上面的问题,我们就需要解决方案:需要考虑,什么时候需要对a提前进行aop生成a的代理对象

Spring面试题随记_第31张图片

Spring面试题随记_第32张图片

 当a出现循环依赖时,就可以考虑对a提前进行aop

二级缓存

Spring面试题随记_第33张图片

为了防止提前AOP生成两个A的代理对象,所以要搞一个二级缓存,让A的代理对象,生成一次后就存起来

Spring面试题随记_第34张图片

C在需要A对象时,就先去二级缓存中查了 

二级缓存的目的:当创建b的过程中,发现a出现了循环依赖,要对a提前进行aop生成代理对象,为了保证后续创建c时同样发现a出现循环依赖,不再次重复生成a的代理对象

earlySingletonObjects专门用来存临时的没有经过完整生命周期的单例bean对象/单例bean代理对象,

Spring面试题随记_第35张图片

Spring面试题随记_第36张图片

创建a的普通对象后,往三级缓存中,放入一个lambda表达式 (此时,表达式还没有执行)

Spring面试题随记_第37张图片

 流程:

创建b时,先去一级缓存池中找a,没找到则再去二级缓存池中找a,如果还没找到,则从三级缓存中把lambda表达式中把a对应的表达式找出来,执行该表达式,如果a身上有aop那么该表达式执行后,就会生成a的代理对象,否则得到的就是a的普通对象。

Spring面试题随记_第38张图片

不管怎样,得到对象后,还是会最后将生成的对象,放入二级缓存中。此时因为对象,还没有经过完整的bean生命周期,所以就放入二级缓存中。

总结:

真正打破循环的,就是第三级缓存

Spring面试题随记_第39张图片

5.5还会把代理对象,从二级缓存中取出来,放入一级缓存单例池中

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

这里,是判断第五步还需不需要进行aop,如果第二步提前进行了aop,那么第五步就不需要再进行aop了

@Async注解为什么会导致循环依赖解决不了?

Spring面试题随记_第40张图片

如果test方法上,既有aop拦截, 又有Async注解,那么就会各自生成一个代理对象,aop生成一个,async注解要生效也是通过生成代理对象,这是两个代理对象,流程走不下去就报错了

构造方法导致循环依赖报错 

Spring面试题随记_第41张图片

报错原因:

Spring面试题随记_第42张图片

卡死在第1步,创建普通对象失败,因为构造a的普通对象时需要传入b,但是没有b,构造b时又需要传入a,所以就直接报错

Spring MVC

Spring面试题随记_第43张图片

Springboot选择tomcat还是jetty

就看pom文件中,引入的是tomcat还是jetty(如果都引入了,就会报错)

Spring面试题随记_第44张图片

ConditionOnClass注解也就是表示,括号内所包含的所有类,都在classpath中出现,这个注解修饰的类,才生效 。这个生效的类中注入的bean,才会真正进入ioc容器中。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

Spring面试题随记_第45张图片

Spring面试题随记_第46张图片

Spring面试题随记_第47张图片

Spring面试题随记_第48张图片

Spring面试题随记_第49张图片

Spring面试题随记_第50张图片

Spring事务怎么实现的

Spring面试题随记_第51张图片

 Transactional注解,为什么会失效

Spring面试题随记_第52张图片

 Spring 中后置处理器的作用

Spring面试题随记_第53张图片

Spring aop怎么实现的

Spring面试题随记_第54张图片

Spring面试题随记_第55张图片

Spring面试题随记_第56张图片

Spring面试题随记_第57张图片

Spring面试题随记_第58张图片

Spring面试题随记_第59张图片

Spring面试题随记_第60张图片

Spring的单例bean不等于单例模式

如何理解spring boot的starter机制

Spring面试题随记_第61张图片

如何实现一个ioc

Spring面试题随记_第62张图片

Spring面试题随记_第63张图片

Spring面试题随记_第64张图片

Spring面试题随记_第65张图片

Ioc实现机制

简单工厂+反射技术

把创建对象,交给工厂来统一管理

Spring面试题随记_第66张图片

上面是简单工厂

Spring面试题随记_第67张图片

Spring面试题随记_第68张图片

上面是通过反射,传入类全路径,通过简单工厂的方式,来返回对象

ioc和DI的区别

ioc是用来解决耦合的一种设计思想,它来集中管理所有对象,而DI是实现ioc中的重要一环,来注入对象。

松耦合和紧耦合

Spring面试题随记_第69张图片

 单一职责

接口分离原则,接口不动,实现换掉就可以了

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

 依赖倒置

Spring面试题随记_第70张图片

BeanFactory作用

Spring面试题随记_第71张图片

Spring面试题随记_第72张图片

BeanFactory也是容器,spring 容器管理着bean的生命周期

BeanDefinition

Spring面试题随记_第73张图片

通过Class.forName得到Class对象,然后通过反射newInstance就可以得到真正的bean对象 Spring面试题随记_第74张图片

 BeanFactory和ApplicationContext上下文区别

共同点:都可以作为容器,都可以管理bean的生命周期

不同点:上下文不直接生产bean,而是通过门面模式,依赖beanFactory来getBean,上下文是更多的和用户打交道的,

Spring面试题随记_第75张图片

上面,写错了是BeanFactory

Spring面试题随记_第76张图片

Spring面试题随记_第77张图片

Spring面试题随记_第78张图片  

被FactoryBean修饰的bean获取,就变成懒加载了,用到时才生成

Spring ioc的加载过程,重点

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

Spring面试题随记_第79张图片

先找到invokeBeanFactoryPostProcessor方法(这个方法,就会把类,转成beanDefinition ),找到componentScan注解包路径,扫描类有没有component注解,有这个注解,就把当前这个类,注册为beanDefinition

然后就是生产bean,交给BeanFactory,通过简单工厂模式,调用getBean方法进行生产,

Spring面试题随记_第80张图片

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

Spring面试题随记_第81张图片

Spring 提供了哪些扩展点

什么是扩展点:spring 对外提供的很多接口或者说钩子方法,当我们实现了这些接口,它就会在特定的点,帮我们执行这些钩子方法,从而我们就可以来对底层进行扩展

 Spring面试题随记_第82张图片

Spring面试题随记_第83张图片

Spring面试题随记_第84张图片

Spring面试题随记_第85张图片

Spring面试题随记_第86张图片

Spring面试题随记_第87张图片

也就是,整个bean加载的各个阶段,都有扩展点

1 bean的注册阶段

2 bean的生产阶段,9次

3 bean的初始化阶段,各种Aware接口

4 初始化阶段,各种生命周期的回调方法(也就是,初始化和销毁阶段的各种回调) 

被spring 容器管理的,从ioc容器中获取的就是spring bean

Spring面试题随记_第88张图片

Java bean 就是自己new出来的

 单例bean的好处

Spring面试题随记_第89张图片

Spring 线程安全问题

Spring面试题随记_第90张图片

Spring面试题随记_第91张图片

自动装配的几种方式

Spring面试题随记_第92张图片

生命周期的几种回调方式

Spring面试题随记_第93张图片

Spring bean在加载过程中,有几种形态

Spring面试题随记_第94张图片

 解释bean的生命周期

Spring面试题随记_第95张图片

Spring 如何避免在并发的情况下,获取到不完整的bean

因为加了双重检查锁

Spring面试题随记_第96张图片

Spring面试题随记_第97张图片

线程2走过一级缓存获取后,发现后续的流程被线程1锁住了,线程2就只能等,等线程1走完后,线程2去二三级缓存中找不到想要的对象,但这是线程2并不会直接开始自己动手创建,而是再次调用getSingleton方法,从一级缓存中获取对象,因为线程1走完流程后,已经把自己创建的对象放入了一级缓存,所以线程2这次就可以过去到对象了,就避免了一级创建

双重检查锁,

为什么不把锁直接加到一级缓存处,因为性能

描述beanDefinition加载过程

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

 Bean工厂的后置处理器接口,就可以完成在所有beanDefinition注册完后的扩展

Spring面试题随记_第98张图片

Spring面试题随记_第99张图片

改成多例bean

如何在spring 所有bean创建完后做扩展

Spring面试题随记_第100张图片

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

方式二,监听refresh 事件,

spring在refresh方法执行完,也就是说所有bean创建完了,会发布一个refresh event事件

Ioc容器加载就是在refresh 方法中完成的

Spring面试题随记_第101张图片

Spring面试题随记_第102张图片

Spring面试题随记_第103张图片

Spring面试题随记_第104张图片

Spring面试题随记_第105张图片

Spring面试题随记_第106张图片

javaConfig是如何替代xml的

Spring面试题随记_第107张图片

Spring面试题随记_第108张图片

Spring面试题随记_第109张图片

Spring面试题随记_第110张图片

两个容器,都有共同点父类AbstractApplicationContext

Spring面试题随记_第111张图片

重点:

在new spring上下文的过程中,在上下文的构造函数中,会注册很多支撑ioc容器整个加载过程的,一些内置的bean处理器

Spring面试题随记_第112张图片

比如上面,解析@Configuration和解析@Autowired注解的处理器

 Spring面试题随记_第113张图片

ConfigurationClassPostprosessor实现BeanDefinitionRegistryPostProcessor的原因,就是为了让自己具有动态地注册BeanDefinition的能力,因为它需要解析@Bean等注解,然后把他们注册成为BeanDefinition

Spring事务的传播行为是怎么实现的

Spring面试题随记_第114张图片

上面的倒数第二行,在内嵌事务函数 最后,会判断内嵌事务的newTransaction属性是否为true,因为此时是融入的事务传播行为方式,所以此时内嵌事务函数,对应的代理对象,在最后就不会执行事务提交。

当带有@Transactional注解的函数A,调用带有@Transactional注解的函数B时,就相当于有两个代理对象,是A的代理对象,调用B的代理对象。

A的代理对象逻辑是创建连接,B的代理对象逻辑是,去ThreadLocal中发现有连接,就知道自己是一个内嵌事务,就去判断发现是融入的传播行为,则将newTransaction属性置为false,这样,B的代理对象逻辑,在最后检查newTransaction属性置为false,也B自己就不去提交事务了,而直接返回A中,A在代理逻辑的最后,统一的去提交事务。

Spring 如何管理mybatis的mapper接口的

Spring面试题随记_第115张图片

因为,mybatis的mapper接口,是没有实现类的,只有接口,我们知道接口是无法实例化,也就无法直接被spring ioc容器进行管理的

Spring面试题随记_第116张图片

通过上面,我们知道spring肯定是管理了UserMapper对应的bean的,不然是不可能通过Autowired注入进来的

Spring面试题随记_第117张图片

上面,是普通类,被scanner扫描并进入spring管理过程的

可以看到,spring默认的ClasspathBeanDefinitiinScanner是不会把接口扫描并注册成BeanDefinition的,所以我们需要自己通过BeanDefinitionRegistryPostProcessor这个扩展接口,来把mybatis的UserMapper接口注册成为spring内的BeanDefinition。

Spring面试题随记_第118张图片

Spring面试题随记_第119张图片

又因为,spring默认的ClasspathBeanDefinitiinScanner在扫描.class文件并注册成BeanDefinition的的过程中,会把接口排除出去,所以我们需要自己写一个Scaner类继承ClasspathBeandefinitionScaner,并覆写它的扫描方法,从而不把接口排除在外

Spring面试题随记_第120张图片

Spring面试题随记_第121张图片

Spring面试题随记_第122张图片

 又因为,接口本身虽然已经扫描,并注册成为了BeanDefinition,但是它是接口,还是不能进行实例化的,所以需要将该BeanDefinition的beanClass属性,替换称为Jdk动态代理的实例

Spring面试题随记_第123张图片

 Spring面试题随记_第124张图片

Spring mvc流程

Spring面试题随记_第125张图片

视图解析器,就是两,字符串类型的视图名hello,返回hello.jsp这个视图View模版,然后最后用数据渲染这个视图模版,成为最后的响应内容

Spring面试题随记_第126张图片

Spring boot整个启动过程,一共发布了9次事件,发布事件就是为了能让外部进行扩展,发布事件对内部来说就是实现解耦

Spring boot内嵌tomcat 启动原理

Spring面试题随记_第127张图片

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

Spring面试题随记_第128张图片

上面,就是通过ConditionOnClass……,来启用了tomcat(这个类是通过上面的上面那幅图中的类引过来的)

TomcatServetWebServerFactory,这个工厂,就可以帮我们创建tomcat并且启动tomcat,

Spring面试题随记_第129张图片

如上,这里就创建了tomcat,上面这个方法,是在spring boot启动的时候调用的

Spring面试题随记_第130张图片

在自动配置类中启用了内嵌tomcat,并通过上面的@Bean注解,给ioc容器中注册了一个tomcat服务的工厂(tomcat server的工厂) 

Spring面试题随记_第131张图片

Spring面试题随记_第132张图片

上面,表示spring boot应用开始启动时, 会先创建spring boot上下文容器,

接着调用spring boot容器类的refresh方法时,来加载真正的spring 容器

Spring面试题随记_第133张图片

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETkDmr4_lpKnpg73lnKjmg7PkvaA,size_20,color_FFFFFF,t_70,g_se,x_16

也就是真正会调用到AbstractApplicationContext的refresh方法,

然后通过533行的方法,会解析@bean等等注解,加载所有的自动配置类,这其中自然就包括web服务器的自动配置类,

因为系统中有Tomcat 相关的类,所以就启用tomcat 相关的自动配置,这样也就把TomcatServetWebServerFactory这个bean对应的BeanDefinition注册到了ioc容器中 Spring面试题随记_第134张图片

 紧接着

Spring面试题随记_第135张图片

 执行545行

Spring面试题随记_第136张图片

Spring面试题随记_第137张图片

Spring面试题随记_第138张图片

会获取web server的工厂,也就是 获取到了前面注册进来的TomcatServetWebServerFactory,

 Spring面试题随记_第139张图片

执行到上面这个getWebServer方法 ,就会创建tomcat并启动tomcat,并让tomcat阻塞住,等待客户端用户发送请求

Spring面试题随记_第140张图片

Spring mvc怎么集成进来的

Spring面试题随记_第141张图片  

Spring面试题随记_第142张图片

Spring boot里面的spring mvc的核心类,DispatcherServlet也是通过自动配置类注入进来的,不是自己以前那样,在web.xml中自己配置的

Spring boot配置文件读取原理和读取顺序

Spring面试题随记_第143张图片

Spring面试题随记_第144张图片

Spring 启动时,会发布上面的事件

Spring面试题随记_第145张图片

这个事件,有7个地方进行了监听,其中第一个就是配置文件监听器,这个监听器就会去加载配置文件,按照上面图中描述的读取顺序读取。

Spring面试题随记_第146张图片

Spring面试题随记_第147张图片

Spring面试题随记_第148张图片

Spring面试题随记_第149张图片

Spring boot默认使用cglib代理,上面就是切换回老的有接口就先使用jdk代理的方式

你可能感兴趣的:(java)