掌握 Spring:从新手到高手的常见问题汇总
一提起Spring,总感觉有太多知识,无法详尽,有些基础理解就先不说了,相信大家都已经用过Spring了
下面简单针对常见Spring面试题做些回答
核心特性
- IOC容器
- spring事件
- 资源管理
- 国际化
- 校验
- 数据绑定
- 类型转换
- spirng表达式
- 面向切面编程
- ……
SpringMVC流程
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
实例化Bean的方法initializeBean,该方法在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory类下
Bean生命周期
-
Spring 容器根据配置中的 bean 定义 实例化 bean。
-
Spring 使用依赖注入填充所有属性,如 bean 中所定义的配置。
-
invokeAwareMethods:如果 bean 实现BeanNameAware 接口,则工厂通过传递 bean 的 ID 来调用setBeanName()。如果bean实现BeanClassLoaderAware,则调用setBeanClassLoader()
-
如果 bean 实现 BeanFactoryAware 接口,工厂通过传递自身的实例来调用 setBeanFactory()。
-
如果存在与 bean 关联的任何BeanPostProcessors,则调用 preProcessBeforeInitialization() 方法
-
如果为 bean 指定了 init 方法( 的 init-method 属性),那么将调用它。
-
最后,如果存在与 bean 关联的任何 BeanPostProcessors,则将调用 postProcessAfterInitialization() 方法
-
如果 bean 实现DisposableBean 接口,当 spring 容器关闭时,会调用 destory()。
-
如果为bean 指定了 destroy 方法( 的 destroy-method 属性),那么将调用它。
SPI机制
基于接口的编程+策略模式+配置文件
在Springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个jar包中搜索所有META-INF/spring.factories
配置文件,其实这里不仅仅是去ClassPath路径下查询,而是会扫描所有路径下的jar包,只不过这个文件只会在ClassPath下的jar包中.
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// spring.factories文件的格式为:key=value1,value2,value3
// 从所有的jar包中找到META-INF/spring.factories文件
// 然后从文件中解析出key=factoryClass类名称的所有value值
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
// 取得资源文件的URL
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
// 遍历所有的URL
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// 根据资源文件URL解析properties文件,得到对应的一组@Configuration类
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
// 组装数据,并返回
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
Bean循环依赖如何解决的
先调用构造函数进行实例化,然后填充属性,再接着进行其他附加操作和初始化,正是这样的生命周期,才有了Spring的解决循环依赖,这样的解决机制是根据Spring框架内定义的三级缓存来实现的,也就是说:三级缓存解决了Bean之间的循环依赖
找出了前面提到的三级缓存,也就是三个Map集合类:
singletonObjects
:第一级缓存,用于保存实例化、注入、初始化完成的bean实例
earlySingletonObjects
:第二级缓存,用于保存实例化完成的bean实例(未注入和初始化)
singletonFactories
:第三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。属于提前暴露
后面专门针对这块分析下源码,下面是调用初始化的详细步骤示例
- 创建对象A,实例化的时候把A对象工厂放入三级缓存
- A注入属性时,发现依赖B,转而去实例化B
- 同样创建对象B,注入属性时发现依赖A,一次从一级到三级缓存查询A,从三级缓存通过对象工厂拿到A,把A放入二级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入一级缓存
- 接着继续创建A,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除二级缓存中的A,同时把A放入一级缓存
- 最后,一级缓存中保存着实例化、初始化都完成的A、B对象
解释 Spring 支持的几种 bean 的作用域。
Spring 框架支持以下五种 bean 的作用域:
- singleton : bean 在每个 Spring ioc 容器中只有一个实例。
- prototype:一个 bean 的定义可以有多个实例。
- request:每次 http 请求都会创建一个 bean,该作用域仅在基于 web的 Spring ApplicationContext 情形下有效。
- session:在一个 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。
- global-session:在一个全局的 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。缺省的 Spring bean 的作用域是 Singleton.
BeanFactory 和 ApplicationContext 的区别
BeanFactory
和 ApplicationContext
都是 Spring 框架提供的 IOC 容器,分别代表了基础的 IOC 容器和高级的 IOC 容器。ApplicationContext
是 BeanFactory
的子接口,继承了 BeanFactory
的所有功能,并在此基础上提供了更多扩展性的服务。
二、BeanFactory
- 基本概念:
BeanFactory
是 Spring 框架中最基础的 IOC 容器,它负责读取 bean 配置文档,管理 bean 的加载与实例化,并控制 bean 的生命周期及维护 bean 间的依赖关系。 - 加载机制:
BeanFactory
采用延迟加载机制,即在真正使用某个 bean 时(通过getBean()
方法调用)才会对其进行加载和实例化。 - 创建方式:
BeanFactory
通常通过编程方式创建。
三、ApplicationContext
- 高级特性:
ApplicationContext
在BeanFactory
的基础上增加了许多高级功能,例如国际化支持、资源文件访问、事件发布机制等。 - 加载机制:
ApplicationContext
在容器启动时,会一次性创建所有非懒加载的 bean。 - 创建方式:除了支持编程式创建之外,
ApplicationContext
还支持声明式创建。 - 扩展功能
- 继承
MessageSource
接口,支持国际化。 - 提供统一的资源文件访问方式。
- 支持在监听器中注册 bean 的事件。
- 允许同时加载多个配置文件。
- 能够载入具有继承关系的多个上下文,使每个上下文专注于特定的层次(如 Web 层)。
四、BeanPostProcessor 和 BeanFactoryPostProcessor 的使用
- 继承
BeanFactory
需要手动注册BeanPostProcessor
和BeanFactoryPostProcessor
。ApplicationContext
则自动注册BeanPostProcessor
和BeanFactoryPostProcessor
。
框架中都用到了哪些设计模式?
-
工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
-
单例模式:Bean默认为单例模式。
-
代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
-
模板方法:用来解决代码重复的问题。比如.JdbcTemplate
-
观察者模式:ApplicationListener。
-
适配器模式:Aop中,使用Advice(通知)来增强被代理类的功能
使用@Autowired 注解自动装配的过程是怎样的?
使用@Autowired 注解来自动装配指定的 bean。在使用@Autowired 注解之前需要在 Spring 配置文件进行配置。
在启动 spring IoC 时,容器自动装载了一个 AutowiredAnnotationBeanPostProcessor 后置处理器,
当容器扫描到@Autowied、@Resource 或@Inject 时,就会在 IoC 容器自动查找需要的 bean,并装配给该对象的属性。在使用@Autowired 时,首先在容器中查询对应类型的 bean:
• 如果查询结果刚好为一个,就将该 bean 装配给@Autowired 指定的数据;
• 如果查询的结果不止一个,那么@Autowired 会根据名称来查找;
• 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用 required=false。
后面看看会针对一些比较常见重要的问题进行更新,今天就到这,总结总结,希望大家一起成长