ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringIOC 面试题(下)

2021-09-09 11:29:49  阅读:192  来源: 互联网

标签:applicationContext 面试题 SpringIOC 依赖 对象 setterServiceInjection public 注入


上篇给大家讲了一下SpringIOC的启动流程,接下来给大家讲讲DI—Dependency Injection(依赖注入)

什么是依赖注入(DI)

DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  • 谁依赖于谁:当然是应用程序依赖于IoC容器;
  • 为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  • 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
  • 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

从依赖注入的方式来说整体可以分为两大类来处理,一种是手动方式,一种是自动方式。

  • 手动方式
    • XML 资源配置元信息(比较常见)
    • Java 注解配置元信息 (比较常见)
    • API 配置元信息(不太常用)
  • 自动方式:
    • Autowiring

依赖注入的方式有上面的两种,但是也可按注入的类型来区分:

  • setter注入
  • 构造器注入
  • 方法注入
  • 接口注入

接下来上代码来加深一下理解

setter

先从注入的类型先分析怎么样的一种方式叫Setter方式注入

/构建一个测试Service
public class SetterServiceInjection {
    public void testMethod(String param) {
        System.out.println(param);
    }
}

public class SetterServiceInjectionTest {
    private SetterServiceInjection setterServiceInjection;

    // Setter方式注入
    public void setSetterServiceInjection(SetterServiceInjection setterServiceInjection) {
        this.setterServiceInjection = setterServiceInjection;
    }

    public void testMethod(){
        setterServiceInjection.testMethod("Setter方式注入");
    }

  
  // 测试启动demo
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        //获取IOC容器中的bean
        SetterServiceInjectionTest serviceInjectionTest = (SetterServiceInjectionTest) applicationContext.getBean("setterServiceInjectionTest");
        serviceInjectionTest.testMethod();
       // 结果打印:
       // Setter方式注入
       
    }
}

xml文件配置

<bean id="setterServiceInjection" class="com.ao.bing.demo.spring.ioc.SetterServiceInjection"/>

<!--Setter方式-->
<bean id="setterServiceInjectionTest" class="com.ao.bing.demo.spring.ioc.SetterServiceInjectionTest">
 <property name="setterServiceInjection" ref="setterServiceInjection"/>
</bean>

上面是很常见的一种注入方式,而且这种方式常见于去写一些配置文件、插件二方包、或者注入数据源信息等。

当然Setter不是仅仅只是这一种使用方式,还可以注入对象,或者说注入一些集合信息等等。

构造器注入

在代码的实现上面构造器和Setter方式是很相似的。还是按照上面的代码改造一下。

 private final SetterServiceInjection setterServiceInjection;

    // Setter方式注入
//    public void setSetterServiceInjection(SetterServiceInjection setterServiceInjection) {
//        this.setterServiceInjection = setterServiceInjection;
//    }

    public void testMethod(){
        setterServiceInjection.testMethod("构造器方式注入");
    }

    //构造器注入
    public SetterServiceInjectionTest(SetterServiceInjection setterServiceInjection){
        this.setterServiceInjection = setterServiceInjection;
    }
 	<context:component-scan base-package="com.ao.bing.demo"/>

    <bean id="setterServiceInjection" class="com.ao.bing.demo.spring.ioc.SetterServiceInjection"/>

    <!--Setter方式-->
<!--    <bean id="setterServiceInjectionTest" class="com.ao.bing.demo.spring.ioc.SetterServiceInjectionTest">-->
<!--        <property name="setterServiceInjection" ref="setterServiceInjection"/>-->
<!--    </bean>-->

    <bean id="setterServiceInjectionTest" class="com.ao.bing.demo.spring.ioc.SetterServiceInjectionTest">
        <constructor-arg index="0" ref="setterServiceInjection"/>
    </bean>

既然两个代码这么相似,为什么Spring官方还需要推荐使用这种方式呢?和Setter方式区别又是啥?

  • 推荐原因:从定义的属性来说添加了final修饰说明我们注入的依赖不能再变动。其次从XML的配置bean的属性来说,当需要实例化setterServiceInjectionTest这个类的时候已经实现了有参构造函数,那么就不会再使用默认的构造函数,同时针对传入的参数需要确保有这种类型的值,否则就会报错,所以这样就保证了依赖不会为空最后因为构造器传入的参数是确定有值的,那就意味着构造属性是已经完全初始化的状态,所以这也就避免了后面需要分析的循环依赖的问题。
  • 区别
    • 在Setter注入,可以将依赖项部分注入,构造方法注入不能部分注入。
    • 使用setter注入不能保证类的所有的属性都注入进来。
    • 在类对象相互依赖的时候可以通过Setter方式解决循环依赖问题。

接口回调注入

提供Spring中获取容器本身的一些功能资源,就是通过实现一系列Spring Aware接口来实现具体的功能。

  • BeanFactoryAware:获取 IoC 容器 - BeanFactory
  • ApplicationContextAware:获取 Spring 应用上下文 - ApplicationContext 对象
  • EnvironmentAware:获取 Environment 对象
  • ResourceLoaderAware:获取资源加载器 对象 - ResourceLoader
  • BeanClassLoaderAware:获取加载当前 Bean Class 的 ClassLoader
  • BeanNameAware:获取当前 Bean 的名称
  • MessageSourceAware:获取 MessageSource 对象,用于 Spring 国际化
  • ApplicationEventPublisherAware:获取 ApplicationEventPublishAware 对象,用于 Spring 事件
  • EmbeddedValueResolverAware:获取 StringValueResolver 对象,用于占位符处理

上面的接口回调实现方式也比较简单,基本所有的bean都能实现Aware接口,但是实现Aware接口也有一定的局限性,不能进行扩展只能是进行内嵌,所以理解这就是一种内建的回调方式。

ApplicationContextAware实现代码为例如下所示

@Component
public class SetterServiceInjectionTest implements ApplicationContextAware {

//    @Autowired
//    private SetterServiceInjection setterServiceInjection;

    private ApplicationContext applicationContext;

    public void testMethod() {
        SetterServiceInjection setterServiceInjection = (SetterServiceInjection) applicationContext.getBean("setterServiceInjection");
        setterServiceInjection.testMethod("接口回调");
    }

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        //获取IOC容器中的bean
        SetterServiceInjectionTest serviceInjectionTest = (SetterServiceInjectionTest) applicationContext.getBean("setterServiceInjectionTest");
        serviceInjectionTest.testMethod();
    }

    // 获取上下文
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

方法注入

方法注入实现方式可以分为四种:

  • @Autowired:是Spring自带的注解,依照类型进行装配。
  • @Bean:产生一个Bean对象,然后这个Bean对象交给Spring管理。
  • @Resource:@Resource`是JavaEE的标准,Spring对它是兼容性的支持,依照名称进行装配。
  • @Inject(不常见):jsr330中的规范。

以常见的Autowired为例

    @Autowired 
    private SetterServiceInjection setterServiceInjection;

    public void testMethod(){
        setterServiceInjection.testMethod("方法注入");
    }
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        //获取IOC容器中的bean
        SetterServiceInjectionTest serviceInjectionTest = (SetterServiceInjectionTest) applicationContext.getBean("setterServiceInjectionTest");
        serviceInjectionTest.testMethod();
    }

从上面的代码中并不需要再写一些构造方法,也不用配置相关XML文件只要简单的加上@Autowired一个注解就能完成bean的相互关联。

所以方法注入可以理解不用关心方法名称也不用关心方法类型,只要方法上面在参数里面有相关的依赖类型同时加上@Autowired或者 @Resource 就能相关联上。

类型选择

上面介绍了这么多类型,那么应该怎么合理的选择哪个依赖的注入类型呢?

  • 构造器注入:强制依赖类型,低依赖。
  • Setter 方法注入:非很强的强制依赖类型(无依赖顺序),多依赖。
  • 方法注入:常用于声明类。
  • 接口回调注入:业务中常用于写一些主键啥的。

合理的选择注入类型能减少业务开发环境中的很多的问题。

总结

Spring的依赖注入用一句话来说解耦对象之间的依赖关系,通过xml方式或者注解的方式来灵活管理依赖。

为了加深理解还给大家整理了一下几个面试题。
构造器注入和 Setter 注入有啥区别?更推荐什么方式?

答案已经在文中构造器的解释中给说出来了

怎么解决多个类型相同的bean注册到Spring容器的使用问题?

可以使用Qualifier注解来实现

对IoC与DI浅显易懂的讲解

  • IoC(控制反转)

首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。

那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

  • DI(依赖注入)

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

标签:applicationContext,面试题,SpringIOC,依赖,对象,setterServiceInjection,public,注入
来源: https://blog.csdn.net/mifenglaozuo/article/details/120194233

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有