当前位置: 首页 > 科技观察

后辈SpringIOC面试题(下)

时间:2023-03-15 15:24:40 科技观察

上一篇文章给后辈讲了SpringIOC的启动过程,今天给后辈讲讲DI——依赖注入(DependencyInjection)。SpringIOC面试题(上)什么是依赖注入?依赖注入(DI)是一个过程,在该过程中,对象只能通过构造函数参数、工厂方法参数或在对象实例上设置或创建属性以定义其依赖项(即,它们与之相关的其他对象)工作)。从工厂方法返回。然后容器在创建bean时注入这些依赖项。从根本上说,这个过程是bean本身的逆过程,它使用类的直接构造或服务定位器模式来控制其依赖项本身的实例化或位置(因此称为控制反转)。使用DI原则,在为对象提供依赖时,代码更干净,解耦更有效。该对象不查找其依赖项,也不知道依赖项的位置或类。因此,您的类变得更容易测试,尤其是当依赖项依赖于接口或抽象基类时,这允许在单元测试中使用存根或模拟实现。----------以上解释出处为Spring官方文档。说白了,依赖注入只是给IOC容器添加bean的一种方式。从依赖注入的方式来说,整体可以分为两类来处理,一类是手动的,一类是自动的。手动方式:XML资源配置元信息(比较常见)Java注解配置元信息(比较常见)API配置元信息(不常见)自动方式:Autowiring依赖注入有上面两种方式,但也可以注入按类型区分:Setter注入、构造函数注入、接口注入、方法注入、依赖注入。首先,我们需要谈谈自动装配模式。Spring官方文档对AutowiringModes的解释:Spring容器可以自动组装bean之间的协作。关系。通过检查ApplicationContext的内容,您可以让Spring自动为您的bean解析协作者(其他bean),并且还建议4种自动装配模式no:(默认)不自动装配。Bean引用必须由ref元素定义。对于大型部署,不建议更改默认设置,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。byName:按属性名自动路由。Spring寻找一个与需要自动装配的属性同名的bean。例如,如果一个bean定义被设置为按名称自动装配,并且包含一个master属性(即,它有一个setMaster(..)方法),那么Spring将查找一个名为master的bean定义并使用它来设置该属性.byType:如果容器中恰好存在该属性类型的一个bean,则导致该属性被自动装配。如果有多个错误,将抛出致命异常,表明您可能没有为该bean使用byType自动装配。如果没有匹配的bean,则什么也不会发生(属性未设置)。constructor:类似于byType,但是用于构造函数参数。如果容器中不存在构造函数参数类型的bean,则会抛出致命错误。虽然官方文档提出了Autowiring自动绑定的方式,但是由于存在一定的局限性,所以在我们实际的业务场景中使用的比较少,而且Spring官方文档也列出了Notenough。自动装配的限制和缺点(官方文档链接)属性和构造函数参数设置中的显式依赖总是覆盖自动装配。您不能自动装配简单的属性,例如基元、字符串和类(以及此类简单属性的数组)。此限制是设计使然。PS:对于这种情况,可以使用另一种方式转换@value等来处理这种场景。自动装配不如显式装配精确。尽管已经说过,Spring还是小心翼翼地避免在可能产生意外结果的模棱两可的情况下进行猜测。不再明确记录SpringManagement对象之间的关系。容器中的多个bean定义可以匹配由setter方法或构造函数参数指定的类型以自动装配。这不一定是数组、集合或Map实例的问题。但是,对于需要单个值的依赖项,这种歧义不会被任意解决。如果没有唯一的bean定义可用,则会引发异常。说了这么多文档的基础知识,接下来就是开始demo测试环节,加深对上面说的这么多的理解。Setter首先分析注入的类型。调用Setter注入/构建测试Service的方法publicclassSetterServiceInjection{publicvoidtestMethod(Stringparam){System.out.println(param);}}publicclassSetterServiceInjectionTest{privateSetterServiceInjectionsetterServiceInjection;//Setter方法注入publicvoidsetSetterServiceInjection(SetterServiceInjectionsetterServiceInjection){this.setterServiceInjection=setterServiceInjection;}publicvoidtestMethod(){setterServiceInjection.testMethod("Setter方法注入");}//测试开始IOC容器serviceInjectionTest=(SetterServiceInjectionTest)applicationContext.getBean("setterServiceInjectionTest");serviceInjectionTest.testMethod();//结果打印://Setter方法注入}}xml文件配置以上是一种很常见的注入方式,这种方式经常被用来写一些配置文件,插件二方包,或者注入数据源信息等,当然Setter不只是这种使用方式,它还可以注入对象,或者注入一些集合信息等等。构造函数注入在代码的实现上与构造函数和Setter方法非常相似。或者按照上面的代码修改如下所示");}//构造函数注入.ao.bing.demo.spring.ioc.SetterServiceInjection"/>既然两个代码这么相似,为什么Spring官方还是需要推荐这个方法吗?和Setter方法有什么区别?推荐理由:从属性的定义来看,最后添加的修饰表示我们注入的依赖不能再改变了。其次,从xml配置bean属性来看,当需要实例化setterServiceInjectionTest类时,已经实现了带参数的构造函数,所以不会再使用默认构造函数,同时需要保证有一个这个类型的值对于传入的参数,否则会报错,所以这个保证依赖不会为空最后,因为构造函数传入的参数是确定有值的,也就是说构造属性是完全初始化的,这样就避免了后面需要分析的循环依赖问题。区别在于Setter注入可以部分注入依赖,而构造函数注入不能部分注入。使用setter注入并不能保证类的所有属性都被注入。当类对象相互依赖时,可以通过Setter解决循环依赖问题。接口回调注入提供了在Spring中获取容器自身的一些功能资源,就是通过实现一系列的SpringAware接口来实现特定的功能。BeanFactoryAware:获取IoC容器-BeanFactoryApplicationContextAware:获取Spring应用上下文-ApplicationContext对象EnvironmentAware:获取Environment对象ResourceLoaderAware:获取资源加载器对象-ResourceLoaderBeanClassLoaderAware:获取加载当前Bean类的ClassLoaderBeanNameAware:获取名称当前BeanMessageSourceAware:获取MessageSource对象,用于Spring国际化ApplicationEventPublisherAware:获取ApplicationEventPublishAware对象,用于Spring事件EmbeddedValueResolverAware:获取StringValueResolver对象,用于占位符处理以上接口回调的实现比较简单,基本上所有bean都可以实现Aware接口,但是Aware接口的实现也有一定的局限性。不能扩展,只能嵌入,所以理解这是一个内置的回调方法。以ApplicationContextAware实现代码为例如下图所示@ComponentpublicclassSetterServiceInjectionTestimplementsApplicationContextAware{//@Autowired//privateSetterServiceInjectionsetterServiceInjection;privateApplicationContextapplicationContext;publicvoidtestMethod(){SetterServiceInjectionsetterServiceInjection=(SetterServiceInjection)applicationContext.getBean("setterServiceInjection");setterServiceInjection.testMethod("接口回调");}publicstaticvoidmain(String[]args){ApplicationContextapplicationContext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");//获取IOC容器中的beanSetterServiceInjectionTestserviceInjectionTest=(SetterServiceInjectionTest)applicationContext.getBean("setterServiceInjectionTest");serviceInjectionTest);testMethod(}//获取上下文@OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{this.applicationContext=applicationContext;}}方法注入方法注入实现分为四种:@Autowired:是Spring自带的注解,按照类型组装@Bean:生成一个Bean对象,然后把Bean对象交给Spring管理。@Resource:@Resource`是JavaEE的标准,Spring为了兼容而支持,按照名字拼装。@Inject(不常见):jsr330中的规范。以常见的Autowired为例@AutowiredprivateSetterServiceInjectionsetterServiceInjection;publicvoidtestMethod(){setterServiceInjection.testMethod("MethodInjection");}publicstaticvoidmain(String[]args){ApplicationContextapplicationContext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");//获取IOC容器beanSetterServiceInjectionTestserviceInjectionTest=(SetterServiceInjectionTest)applicationContext.getBean("setterServiceInjectionTest");serviceInjectionTest.testMethod();}从上面的代码来看,不需要写一些构造方法,也不需要配置相关的XML文件,只需要简单的加上@Autowired一个注解就可以完成相互关联豆子。所以方法注入可以理解为不需要关心方法名,也不需要关心方法类型,只要方法在参数中有相关的依赖类型,关联@Autowired或@Resource即可。类型选择上面介绍了这么多类型,那么如何合理选择依赖注入类型呢?构造函数注入:强制依赖类型,低依赖。setter方法注入:非强强制依赖类型(无依赖顺序),多重依赖。方法注入:常用于类的声明接口回调注入:业务中常用于写一些主键。合理选择注入类型可以减少业务开发环境中的很多问题。在实际业务场景中,还会遇到另外一个问题,就是在Spring容器中注册了多个同类型的bean,那么只使用上面的方法就会抛出NoUniqueBeanDefinitionException,所以为了解决上面的问题Spring提出了一个新的注解**@Qualifier**指定是哪个bean或者实现bean的逻辑分组,其用法比较单一publicclassQualifierDemo{@AutowiredprivateListdemos;//1,2,3,4都有@Autowired@QualifierprivateListdemosQualifier;//只有3,4@Autowired@Qualifier("demo2")privateDemodemo1;//只有2@BeanpublicDemodemo1(){returnnewDemo(1);}@BeanpublicDemodemo2(){returnnewDemo(2);}@Bean@Qualifier//用于逻辑分组publicDemodemo3(){returnnewDemo(3);}@Bean@Qualifier//用于逻辑分组publicDemodemo4(){returnnewDemo(4);}@DatapublicclassDemo{privateIntegerid;publicDemo(Integerid){this.id=id;}}}通过上面的代码可以清楚的知道如果不使用Qualifierannotation,默认是加载所有demosQualifier。带有Qualifier注解的demoQualifier中只有demo3和demo4。一,你也可以指定使用demo1所示的bean。当然,这只是介绍了Qualifier的简单实用性。在Spring的官方文档中,还有一种用法是实现Qualifier扩展用法和自定义注解。了解SpringCloud的同学可以看看@LoadBalanced注解。用法如下@Target({ElementType.FIELD,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documented@Qualifierpublic@interfaceDemoGroup{}Spring依赖注入大家差不多都讲完了,当然还有一些其他的比较少见,比如对延迟依赖注入感兴趣的可以再看一遍。推荐使用ObjectProvider方法来处理。总结一下Spring的依赖注入,一句话,解耦对象之间的依赖关系,通过xml或者注解灵活管理依赖关系。这里看框架的东西,建议大家去看官方文档。如果不懂英文,可以找中文翻译加深理解。(中文官方文档链接)。接下来分析一下Spring中的三层缓存是如何解决循环依赖的。为了加深理解,我也整理了几道面试题给大家。构造函数注入和Setter注入有什么区别?推荐的方法是什么?文中对构造函数的解释中已经给出了答案。如何解决将多个相同类型的bean注册到Spring容器中的问题?可以使用Qualifier注解来实现参考文档:中文官方文档,《小马哥核心编程》。最近在做的采访版PDF,真的很有意思。出来的时候,应该可以让大家在采访前大吃一惊。顺便说一句,采访视频正在计划中。这次我打算用不同的风格来表演。下个月我一定会去做。可以出来。我是敖丙,知道的越多,不知道的越多,下期见。