20190514面试SPRING的IOC

1.什么是IOC

IoC(Inversion of Control)控制反转,包含了两个方面:一、控制。二、反转

  • 控制指的是:当前对象对内部成员的控制权
  • 反转指的是:这种控制权不由当前对象管理了,由其他(类,第三方容器)来管理。

.IOC和DI的区别:

(1)Ioc:控制反转,把创建对象交给Spring进行配置

(2)DI:依赖注入,向类里面的属性中设置值

(3)二者关系:DI不能单独存在,要在Ioc基础之上来完成操作,即要先创建对象才能注入属性值

如何实现依赖注入

  • 构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
  • Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。
  • 接口注入:这个是遗留下来的,很少用的了!


两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖

 

2.IOC的好处

  1. 不用自己组装,拿来就用。
  2. 享受单例的好处,效率高,不浪费空间。
  3. 便于单元测试,方便切换mock组件。
  4. 便于进行AOP操作,对于使用者是透明的。
  5. 统一配置,便于修改。

3.IOC实现原理

IOC容器其实就是一个大工厂,它用来管理我们所有的对象以及依赖关系。

  • 原理就是通过Java的反射技术来实现的!通过反射我们可以获取类的所有信息(成员变量、类名等等等)!
  • 再通过配置文件(xml)或者注解来描述类与类之间的关系
  • 我们就可以通过这些配置信息和反射技术来构建出对应的对象和依赖关系了!
  •  

详细

Spring IOC容器是怎么实现对象的创建和依赖的

  1. 根据Bean配置信息在容器内部创建Bean定义注册表
  2. 根据注册表加载、实例化bean、建立Bean与Bean之间的依赖关系
  3. 将这些准备就绪的Bean放到Map缓存池中,等待应用程序调用

Spring容器(Bean工厂)可简单分成两种:

  • BeanFactory
    • 这是最基础、面向Spring的
  • ApplicationContext
    • 这是在BeanFactory基础之上,面向使用Spring框架的开发者。提供了一系列的功能!

几乎所有的应用场合都是使用ApplicationContext!

BeanFactory的继承体系

ApplicationContext的继承体系:

其中在ApplicationContext子类中又有一个比较重要的:WebApplicationContext

Web应用与Spring融合:

我们看看BeanFactory的生命周期:

接下来我们再看看ApplicationContext的生命周期:

初始化的过程都是比较长,我们可以分类来对其进行解析:

  • Bean自身的方法:如调用 Bean 构造函数实例化 Bean,调用 Setter 设置 Bean 的属性值以及通过的 init-method 和 destroy-method 所指定的方法;
  • Bean级生命周期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,这些接口方法由 Bean 类直接实现;
  • 容器级生命周期接口方法:在上图中带“★” 的步骤是由 InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“ 后处理器” 。 后处理器接口一般不由 Bean 本身实现,它们独立于 Bean,实现类以容器附加装置的形式注册到Spring容器中并通过接口反射为Spring容器预先识别。当Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用,所以这些后处理器的影响是全局性的。当然,用户可以通过合理地编写后处理器,让其仅对感兴趣Bean 进行加工处理

ApplicationContext和BeanFactory不同之处在于:

  • ApplicationContext会利用Java反射机制自动识别出配置文件中定义的BeanPostProcessor、 InstantiationAwareBeanPostProcesso 和BeanFactoryPostProcessor后置器,并自动将它们注册到应用上下文中。而BeanFactory需要在代码中通过手工调用addBeanPostProcessor()方法进行注册
  • ApplicationContext在初始化应用上下文的时候就实例化所有单实例的Bean。而BeanFactory在初始化容器的时候并未实例化Bean,直到第一次访问某个Bean时实例化目标Bean。

有了上面的知识点了,我们再来详细地看看Bean的初始化过程:

简要总结:

  • BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
  • 容器扫描BeanDefinitionRegistry中的BeanDefinition;调用InstantiationStrategy进行Bean实例化的工作;使用BeanWrapper完成Bean属性的设置工作;
  • 单例Bean缓存池:Spring 在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用HashMap实现的缓存器,单实例的Bean以beanName为键保存在这个HashMap中。

装配Bean方式

Spring4.x开始IOC容器装配Bean有4种方式:

  • XML配置
  • 注解
  • JavaConfig
  • 基于Groovy DSL配置(这种很少见)

依赖注入方式

依赖注入的方式有3种方式:

  • 属性注入-->通过setter()方法注入
  • 构造函数注入
  • 工厂方法注入

总的来说使用属性注入是比较灵活和方便的,这是大多数人的选择!

对象之间关系

<bean>对象之间有三种关系:

  • 依赖-->挺少用的(使用depends-on就是依赖关系了-->前置依赖【依赖的Bean需要初始化之后,当前Bean才会初始化】)
  • 继承-->可能会用到(指定abstract和parent来实现继承关系)
  • 引用-->最常见(使用ref就是引用关系了)

Bean的作用域

Bean的作用域:

  • 单例Singleton
  • 多例prototype
  • 与Web应用环境相关的Bean作用域
    • reqeust
    • session

使用到了Web应用环境相关的Bean作用域的话,是需要我们手动配置代理

原因也很简单:因为我们默认的Bean是单例的,为了适配Web应用环境相关的Bean作用域--->每个request都需要一个对象,此时我们返回一个代理对象出去就可以完成我们的需求了!

将Bean配置单例的时候还有一个问题:

  • 如果我们的Bean配置的是单例,而Bean对象里边的成员对象我们希望是多例的话。那怎么办呢??
  • 默认的情况下我们的Bean单例,返回的成员对象也默认是单例的(因为对象就只有那么一个)!

此时我们需要用到了lookup方法注入,使用也很简单,看看例子就明白了:

处理自动装配的歧义性

 

结合两本书的知识点,可以归纳成两种解决方案:

  • 使用@Primary注解设置为首选的注入Bean
  • 使用@Qualifier注解设置特定名称的Bean来限定注入!
    • 也可以使用自定义的注解来标识

如何获取ApplicationContext


	public static void main(String[] args) {
		
        //1.通过构造函数加载配置类
         ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf.class);

        //2.通过编码方式注册配置类
		 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		 ctx.register(DaoConfig.class);
		 ctx.register(ServiceConfig.class);
		 ctx.refresh();

        //3.通过XML组装@Configuration配置类所提供的配置信息
		 ApplicationContext ctx = new ClassPathXmlApplicationContext("com/smart/conf/beans2.xml");

        //4.通过@Configuration组装XML配置所提供的配置信息
		 ApplicationContext ctx = new AnnotationConfigApplicationContext(LogonAppConfig.class);

		 //5.@Configuration的配置类相互引用
		 ApplicationContext ctx = new AnnotationConfigApplicationContext(DaoConfig.class,ServiceConfig.class);
         LogonService logonService = ctx.getBean(LogonService.class);
         System.out.println((logonService.getLogDao() !=null));
         logonService.printHelllo();   
	}

 

 

 

  • 方法替换
    • 使用某个Bean的方法替换成另一个Bean的方法
  • 属性编辑器
    • Spring可以对基本类型做转换就归结于属性编辑器的功劳!
  • 国际化
    • 使用不同语言(英语、中文)的操作系统去显式不同的语言
  • profile与条件化的Bean
    • 满足了某个条件才初始化Bean,这可以方便切换生产环境和开发环境~
  • 容器事件
    • 类似于我们的Servlet的监听器,只不过它是在Spring中实现了~

解释不同方式的自动装配

解释不同方式的自动装配

  • no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配。
  • byName:通过参数名 自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。
  • byType::通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。
  • constructor:这个方式类似于byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
  • autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。

只用注解的方式时,注解默认是使用byType的

参考

https://blog.csdn.net/github_36849773/article/details/70154050

https://javadoop.com/post/spring-ioc

https://juejin.im/post/5b040cf66fb9a07ab7748c8b

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值