SpringIOC 面试题(上)
标签: SpringIOC 面试题(上) Java博客 51CTO博客
2023-07-12 18:24:22 97浏览
Spring是什么
spring 首先它是一个框架,在我们的开发工作的环境中,所有的其他的框架基本都依赖Spring,spring起着一个容器的作用,用来承载我们整体的bean对象。它帮我们整理了整个bean的从创建到销毁的管理。
Spring的核心组件
- Core Container Core Container包含了spring-core, spring-beans, spring-context, spring-context-support, spring-expression (Spring Expression Language) 模块,spring-core和spring-beans模块提供了框架的基本部分,包括IoC(控制反转)和DI(依赖项注入)特性。Context模块模构建于Core和Beans模块基础之上,提供了一种类似JNDI注册器的框架式的对象访问方法;Context 模块继承了Beans的特性,为 Spring 核心提供了大量扩展,添加了对国际化(例如资源绑定)、事件传播、资源加载和对Context的透明创建的支持。Context模块同时也支持J2EE的一些特性,ApplicationContext接口是Context模块的关键;SpEL模块提供了强大的表达式语言,用于在运行时查询和操纵对象。
- AOP and Instrumentation spring-aop模块提供了一个AOP规范的面向切面的编程方式,允许您定义方法拦截器和切入点,以便处理一些横切性问题,比如日志,权限,数据验证,监控性能等。Aspects模块提供了对AspectJ的支持;Instrumentation模块提供了class instrumentation支持和classloader实现,使用 Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。有了这样的功能,开发者就可以实现更为灵活的运行时虚拟机监控和Java类操作了,这样的特性实际上提供了一种虚拟机级别支持的 AOP 实现方式。
- Messaging Spring消息模块,其中包含来自Spring集成项目的关键抽象,如Message、MessageChannel、MessageHandler等,可以作为基于消息的应用程序的基础。
- Data Access/Integration Data Access/Integration包含了JDBC, ORM, OXM, JMS, 和Transaction模块,spring-jdbc模块提供了一个JDBC抽象层,消除了对特定于数据库供应商的错误代码进行冗长的JDBC编码和解析的需要;spring-tx模块支持对实现特殊接口的类和所有pojo的编程和声明性事务管理;spring-orm模块为目前流行的对象关系映射api(包括JPA和Hibernate)提供集成层。使用spring-orm模块,您可以将所有这些ORM映射框架与Spring提供的所有其他特性结合使用,比如前面提到的简单声明性事务管理特性;spring-oxm模块提供了一个抽象层,支持对象/XML映射实现,如JAXB、Castor、XMLBeans、JiBX和XStream;spring-jms模块(Java消息传递服务)包含用于生成和消费消息的特性。
- Web Web层由spring-web、spring-webmvc、spring-websocket和spring-webmvc-portlet模块组成,spring-web模块提供了基本的面向web的集成特性,比如多部分文件上传功能,以及使用Servlet侦听器和面向web的应用程序上下文初始化IoC容器;Spring -webmvc模块(也称为Web- servlet模块)包含Spring的模型-视图-控制器(MVC)和Web应用程序的Rest Web服务实现;spring-webmvc- Portlet模块(也称为Web-Portlet模块)提供了要在Portlet环境中使用的MVC实现,并反映了基于servlet的spring-webmvc模块的功能。
- Test Spring -test模块支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。它提供了Spring applicationcontext的一致加载和这些上下文的缓存。它还提供了模拟对象,您可以使用这些对象独立写单元测试代码。
Spring的核心
核心就是管理资源组件及其依赖关系。包括IoC(Inversion of Control 控制反转)/ DI (Dependency Injection依赖注入),Aop(Aspect Oriented Programming面向切面编程)。
IOC控制反转是啥?
类的创建、销毁都由 Spring 来控制,也就是说控制对象生存周期的不再是引用它的对象,而是 Spring来控制整个过程。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 Spring 控制。
如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
- 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
- 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
IOC能干什么?
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
看到这里其实这都是一些简单的理解,以及一些官方的说法,为了真正的搞懂什么是SpringIoc,就上面的这些东西是远远不够的,所以我给大家画了一个流程图,跟着这个流程图,我们一步一步来解析IOC。
在看一些架构的源码的时候,大家一定要先理清整体架构的脉络,这样才能方便我们理解整个架构,否则就是一面茫然
第一个图我们从左到右,第二个图我们从上到下来讲解
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}
我们以开始启动spring容器开始,常见配置bean有 XML 配置文件的形式或者注解形式等还有一些其他的方式。
不管哪种方式,spring考虑到扩展性问题,会通过BeanDefinitionReader,来加载bean的配置信息,然后生成一个BeanDefinition(bean的定义信息,用来存储 bean 的所有属性方法定义)。
BeanDefinitionReader 只是接口约束一些定义信息,常见的实现类 XmlBeanDefinitionReader(xml形式),PropertiesBeanDefinitionReader(Properties配置文件),AbstractBeanDefinitionReader (相关一些环境信息)等。
BeanFactoryPostProcesser
说完了BeanDefinition那么接下来就是走到BeanFactoryPostProcessor
BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,其实就是在bean的实例化之前,可以获取bean的定义信息,以及修改相关信息。
BeanFactoryPostProcessor有一堆的实现子类,因此当我们有自己的业务逻辑实现的时候也只需要实现BeanFactoryPostProcessor就可以了,然后加上@Component注解就可以了。
BeanFactory
BeanFactory,从名字上也很好理解,生产 bean 的工厂,它负责生产和管理各个 bean 实例。同时也是Spring容器暴露在外获取bean的入口,BeanFactory的生产过程其实是利用反射机制实现的。
我们再来看一下BeanFactory的继承关系
这张关系图我们只要了解的几个关键点
- HierarchicalBeanFactory:提供父容器的访问功能。
- ListableBeanFactory:提供了批量获取Bean的方法。
- AutowireCapableBeanFactory:在BeanFactory基础上实现对已存在实例的管理。
- ConfigurableBeanFactory:单例bean的注册以及生成实例,统计单例bean等信息。
- ConfigurableListableBeanFactory:增加了一些其他功能:类加载器、类型转化、属性编辑器、BeanPostProcessor、bean定义、处理bean依赖关系、 bean如何销毁等等一些还有其他的功能。
- DefaultListableBeanFactory:实现BeanFactory所有功能同时也能注册BeanDefinition。
可能有人要问了,ApplicationContext和BeanFactory是不是只是继承关系?
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
BeanFactory factory = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
BeanFactory是一个底层的IOC容器,而ApplicationContext是在其基础上增加了一些它的特性的同时同时增加了一些其他的整合特性比如:更好的整合SpringAOP、国际化消息、以及事务的发布、资源访问等这些新的特性。
所以BeanFactory和ApplicationContext不是同一个东西,是两个不同的对象,想要获取BeanFactory可以通过applicationContext.getParentBeanFactory()获取。
所以当通过XML来配置bean的信息的时候我们就可以使用BeanFactory作为容器,因为我们不需要有那么多其他的额外的一些特性。当我们通过注解的形式来注册bean信息的时候,我们就可以使用ApplicationContext来作为容器。当然这个只是作为了解,在我们的业务代码中基本是可以不用关心这一点的。
Bean的生命周期
Spring Bean的生命周期在spring的面试题中这其实是非常常见的一道面试题,其实并不用去背那么多流程,在Spring的源码中其实已经写好了bean的完整生命流程,上面的BeanFactory中已经表明。
- BeanNameAware#setBeanName:在创建此bean的bean工厂中设置bean的名称,在普通属性设置之后调用,在InitializinngBean.afterPropertiesSet()方法之前调用
- BeanClassLoaderAware#setBeanClassLoader:将 bean ClassLoaderr 提供给 bean 实例的回调
- BeanFactoryAware#setBeanFactory:回调提供了自己的bean实例工厂,在普通属性设置之后,在InitializingBean.afterPropertiesSet()或者自定义初始化方法之前调用
- org.springframework.context.ResourceLoaderAware#setResourceLoader:在普通bean对象之后调用,在afterPropertiesSet 或者自定义的init-method 之前调用,在 ApplicationContextAware 之前调用。
- org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher:在普通bean属性之后调用,在初始化调用afterPropertiesSet 或者自定义初始化方法之前调用。在 ApplicationContextAware 之前调用。
- org.springframework.context.MessageSourceAware#setMessageSource:在普通bean属性之后调用,在初始化调用afterPropertiesSet 或者自定义初始化方法之前调用,在 ApplicationContextAware 之前调用。
- org.springframework.context.ApplicationContextAware#setApplicationContext:在普通Bean对象生成之后调用,在InitializingBean.afterPropertiesSet之前调用或者用户自定义初始化方法之前。在ResourceLoaderAware.setResourceLoader,ApplicationEventPublisherAware.setApplicationEventPublisher,MessageSourceAware之后调用
- org.springframework.web.context.ServletContextAware#setServletContext:运行时设置ServletContext,在普通bean初始化后调用
- org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization:将此BeanPostProcessor 应用于给定的新bean实例InitializingBean#afterPropertiesSet:在设置所有 bean 属性后由包含的 BeanFactory调用
- org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName:获取InitMethodName名称,并且运行初始化方法
- org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitializationDisposableBean#destroy:销毁
- org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName:返回被销毁的bean名称
这其实就是bean的整个生命周期过程,其实这里面注视大家都是可以自己查看的,每一个方法上面都是很详细注释,我也只是根据注视简单的翻译了一下。
整个过程bean的生命周期可以缩短理解为:
但是要完全理解Spring,那肯定就要说Spring里面的一个非常重要的方法
ApplicationContext.refresh这其中的包含了13个子方法:
public void refresh() throws BeansException, IllegalStateException {
// 添加一个synchronized 防止出现refresh还没有完成出现其他的操作(启动,或者销毁)
synchronized (this.startupShutdownMonitor) {
// 1.准备工作
// 记录下容器的启动时间、
// 标记“已启动”状态,关闭状态为false、
// 加载当前系统属性到环境对象中
// 准备一系列监听器以及事件集合对象
prepareRefresh();
// 2. 创建容器对象:DefaultListableBeanFactory,加载XML配置文件的属性到当前的工厂中(默认用命名空间来解析),就是上面说的BeanDefinition(bean的定义信息)这里还没有初始化,只是配置信息都提取出来了,(包含里面的value值其实都只是占位符)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. BeanFactory的准备工作,设置BeanFactory的类加载器,添加几个BeanPostProcessor,手动注册几个特殊的bean等
prepareBeanFactory(beanFactory);
try {
// 4.子类的覆盖方法做额外的处理,就是我们刚开始说的 BeanFactoryPostProcessor ,具体的子类可以在这步的时候添加一些特殊的BeanFactoryPostProcessor完成对beanFactory修改或者扩展。
// 到这里的时候,所有的Bean都加载、注册完成了,但是都还没有初始化
postProcessBeanFactory(beanFactory);
// 5.调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 6.注册 BeanPostProcessor 处理器 这里只是注册功能,真正的调用的是getBean方法
registerBeanPostProcessors(beanFactory);
// 7.初始化当前 ApplicationContext 的 MessageSource,即国际化处理
initMessageSource();
// 8.初始化当前 ApplicationContext 的事件广播器,
initApplicationEventMulticaster();
// 9.从方法名就可以知道,典型的模板方法(钩子方法),感兴趣的同学还可以再去复习一下之前写的设计模式中的-模版方法模式
// 具体的子类可以在这里初始化一些特殊的Bean(在初始化 singleton beans 之前)
onRefresh();
// 10.注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过
registerListeners();
// 11.初始化所有的 singleton beans(lazy-init 的除外),重点关注
finishBeanFactoryInitialization(beanFactory);
// 12.广播事件,ApplicationContext 初始化完成
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 13.销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
destroyBeans();
cancelRefresh(ex);
// 把异常往外抛
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
这里只是大致的说明一下这里的每个方法的用途,如果还想要了解的更深,就需要大家自己再去看这里面的更深成次的代码了,这个大家可以自己尝试的断点试一下。或者后面再单独给大家写一篇这里面的细节流程。
断点看源码不必要每个方法都去看,先了解一个大概,然后再多断点几次,每次断点都相对上一次进入的更深成次一点,满满的你就能全部理解了。这是一个漫长的过程。
总结
总结一下比较常见的面试题,加深一下印象:
- BeanFactory和ApplicationContext的区别?BeanFactory是一个底层的IOC容器,而ApplicationContext是在其基础上增加了一些它的特性的同时同时增加了一些其他的整合特性比如:更好的整合SpringAOP、国际化消息、以及事务的发布、资源访问等这些新的特性。
- BeanFactory 与 FactoryBean的区别?BeanFactory 是 IoC 底层容器 ,FactoryBean 是 创建 Bean 的一种方式,帮助实现复杂的初始化逻辑。
- Spring IoC 容器的启动过程?这个问题只要看懂了第一张流程图,以及最后的ApplicationContext.refresh()方法中的内部13个子方法,再回答这个问题应该问题不大,面试官应该会眼前一亮,小伙子不错呀!!!~
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论