谈谈SpringFramework与IoC依赖查找

生存不会依照你想要的形式进行,它会给你一段时间,让你孤单又怅惘,等你度过高潮,那些独处的时光必然能照亮你的路。走得最急的,都是最美的风光;伤得最深的,也总是那些最真的感情。拾掇起情绪,持续向前走,就会发现:错过花,你将播种雨,错过雨,你会遇到彩虹。

1. 面试题

先说下该篇文章可延长出的面试题.

1. 谈谈SpringFramework / 说说你了解的SpringFramework

SpringFramework 是一个开源的、松耦合的、分层的、可配置的一站式企业级 Java 开发框架,它的外围是 IOC 与 AOP ,它能够更容易的构建出企业级 Java 利用,并且它能够依据利用开发的组件须要,整合对应的技术。

松耦合的: 为了形容IOC和AOP, 可能会延长出IOC松耦合相干内容
可配置: 给前面的SpringBoot(约定大于配置)做铺垫
IOC 与 AOP: Inverse of Control 管制反转、Aspect Oriented Programming 面向切面编程


2. 为何应用SpringFramework

可通过如下几点进行形容:

  1. IOC 实现了组件之间的解耦
  2. AOP 切面编程将利用业务做对立或特定的性能加强, 可实现利用业务与加强逻辑的解耦
  3. 容器治理利用中应用的Bean、托管Bean的生命周期、事件与监听的驱动机制
  4. Web、事务管制、测试、与其余技术的整合

3. SpringFramework蕴含哪些模块?

  • beans、core、context、expression 【外围包】
  • aop 【切面编程】
  • jdbc 【整合 jdbc 】
  • orm 【整合 ORM 框架】
  • tx 【事务管制】
  • web 【 Web 层技术】
  • test 【整合测试】
  • ......

4. 依赖查找与依赖注入的比照

作用指标实现形式
依赖查找(DL)通常是类成员应用上下文(容器)被动获取
依赖注入(DI)能够是办法体内也能够是办法体外借助上下文被动的接管

5. BeanFactory与ApplicationContext的比照

BeanFactory 接口提供了一个形象的配置和对象的管理机制,

ApplicationContext 是 BeanFactory 的子接口,它简化了与 AOP 的整合、音讯机制、事件机制,以及对 Web 环境的扩大( WebApplicationContext 等)

ApplicationContext 次要扩大了以下性能:

  • AOP 的反对( AnnotationAwareAspectJAutoProxyCreator 作用于 Bean 的初始化之后 )
  • 配置元信息( BeanDefinitionEnvironment 、注解等 )
  • 资源管理( Resource 形象 )
  • 事件驱动机制( ApplicationEventApplicationListener
  • 音讯与国际化( LocaleResolver
  • Environment 形象( SpringFramework 3.1 当前)

2. SpringFramework发展史

在Spring技术之前,J2EE衰亡,过后的J2EE学习老本极高,开发速度慢,开发进去的程序性能耗费也高,曾经跟不上过后应用程序的须要。
在2002 年,Rod Johnson写了一本书名为《Expert One-on-One J2EE design and development》 ,书中对过后现有的 J2EE 利用的架构和EJB框架存在的臃肿、低效等问题提出了质疑,并且踊跃寻找和摸索解决方案。

基于一般Java类和依赖注入的思维提出了更为简略的解决方案,这便是Spring框架核心思想的萌芽

过了 2 年,2004 年 SpringFramework 1.0.0 横空出世,随后 Rod Johnson 又写了一本书《Expert one-on-one J2EE Development without EJB》,过后在 J2EE 开发界引起了微小轰动,这本书中间接通知开发者齐全能够不应用 EJB 开发 J2EE 利用,而是能够换用一种更轻量级、更简略的框架来代替,那就是 SpringFramework

那时在开发界是种种的质疑,大略是这样的,纳尼? 质疑IBM诸多大佬的设计精髓,这个是什么人?为何如此嚣张? 而后 还是被一些开发者尝试应用了,应用后发现的确要比EJB好用,不那么臃肿,性能也有所改善,提供的一些个性也优于EJB,于是就缓缓转投SpringFramework

上面展现下SpringFramework重要版本的更新工夫及次要个性

SpringFramework版本对应jdk版本重要个性
SpringFramework 1.xjdk 1.3基于 xml 的配置
SpringFramework 2.xjdk 1.4改进 xml 文件、初步反对注解式配置
SpringFramework 3.xJava 5注解式配置、JavaConfig 编程式配置、Environment 形象
SpringFramework 4.xJava 6SpringBoot 1.x、外围容器加强、条件拆卸、WebMvc 基于 Servlet3.0
SpringFramework 5.xJava 8SpringBoot 2.x、响应式编程、SpringWebFlux、反对 Kotlin

3. IOC依赖查找

根底框架搭建

  1. 创立Maven模块,这里以ioc-learning为例
  2. 引入依赖
<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-context</artifactId>    <version>5.2.8.RELEASE</version></dependency>
  1. 创立配置文件 ioc-learning-dl.xml
<?xml version="1.0" encoding="UTF-8"?>   <beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans           https://www.springframework.org/schema/beans/spring-beans.xsd">      </beans>
  1. 申明一般类Person.java
public class Person {}
  1. ioc-learning-dl.xml配置文件退出Persion的申明
<bean id="person" class="com.huodd.bean.Person"></bean>
  1. 创立启动类
public class DlApplication {    public static void main(String[] args) {        // 读取配置文件  应用接口 BeanFactory 接管         BeanFactory factory = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml");        // 通过配置文件中申明的 id 进行对象的获取        Person person = (Person) factory.getBean("person");        System.out.println(person);    }}
  1. 运行打印
com.huodd.bean.Person@57baeedf

胜利打印出 Person 的全限定类名 + 内存地址,证实编写胜利。

3.1 byName 名称查找

上述根底框架中的步骤6

外围代码

Person person = (Person) factory.getBean("person");

3.2 byType 类型查找

1. 一般类

  1. 批改配置文件 ioc-learning-dl.xmlperson的申明中id属性去掉
<bean class="com.huodd.bean.Person"></bean>
  1. 批改启动类
public static void main(String[] args) {        BeanFactory factory = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml");//        Person person = (Person) factory.getBean("person");        Person person = factory.getBean(Person.class);        System.out.println(person);    }

getBean办法参数中间接传入Class类型 返回值也无需再进行强转

  1. 运行main办法 打印如下
com.huodd.bean.Person@61862a7f

2. 接口

  1. 创立接口demoDao 以及 实现类 DemoDaoImpl
public interface DemoDao {    List<String> findAll();}public class DemoDaoImpl implements DemoDao{    @Override    public List<String> findAll() {        return Arrays.asList("user1", "user2", "user3");    }}
  1. 批改配置文件 ioc-learning-dl.xml 退出 DemoDaoImpl的申明
<bean class="com.huodd.dao.DemoDaoImpl"></bean>
  1. 批改启动类
 public static void main(String[] args) {        BeanFactory factory = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml");        DemoDao demoDao = factory.getBean(DemoDaoImpl.class);        System.out.println(demoDao);        System.out.println(demoDao.findAll());    }
  1. 运行main办法 打印后果如下
com.huodd.dao.DemoDaoImpl@7334aada[user1, user2, user3]

由此可见 DemoDaoImpl 注入胜利 并且BeanFactory能够依据接口类型找到对应的实现类

3.3 高级查找

ofType 依据类型查找多个

如果一个接口有多个实现类,如何一次性的把所有的实现类都取出来呢? 后面用到的getBean办法显然无奈满足 需应用到ofType办法

  1. 继下面的代码 创立2个DemoDao的实现类 如下
public class DemoMysqlDaoImpl implements DemoDao {    @Override    public List<String> findAll() {        return Arrays.asList("mysql_user1", "mysql_user2", "mysql_user3");    }}public class DemoOracleDaoImpl implements DemoDao {    @Override    public List<String> findAll() {        return Arrays.asList("oracle_user1", "oracle_user2", "oracle_user3");    }}
  1. 批改配置文件 ioc-learning-dl.xml 退出新建的两个实现类的申明
 <bean class="com.huodd.dao.impl.DemoMysqlDaoImpl"></bean> <bean class="com.huodd.dao.impl.DemoOracleDaoImpl"></bean>
  1. 批改启动类
public static void main(String[] args) {        ApplicationContext ctx = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml");        Map<String, DemoDao> beans = ctx.getBeansOfType(DemoDao.class);        beans.forEach((beanName, bean) -> {            System.out.println(beanName + " : " + bean.toString());        });    }

运行main办法 打印后果如下

com.huodd.dao.impl.DemoMysqlDaoImpl#0 : [mysql_user1, mysql_user2, mysql_user3]com.huodd.dao.impl.DemoOracleDaoImpl#0 : [oracle_user1, oracle_user2, oracle_user3]

仔细的小伙伴可能会发现 为何这里读取配置文件的返回值应用的是ApplicationContext 而不应用BeanFactory

ApplicationContext 也是一个接口,通过IDEA的diagram查看类的继承链,能够看到该接口继承了BeanFactory

官网文章中有这样的解释:

org.springframework.beansorg.springframework.context 包是 SpringFramework 的 IOC 容器的根底。BeanFactory 接口提供了一种高级配置机制,可能治理任何类型的对象。ApplicationContextBeanFactory 的子接口。它减少了:

  • 与 SpringFramework 的 AOP 性能轻松集成
  • 音讯资源解决(用于国际化)
  • 事件公布
  • 应用层特定的上下文,例如 Web 应用程序中应用的 WebApplicationContext

如此说来 ApplicationContext 蕴含了 BeanFactory 的所有性能,并且还扩大了更多的个性

其实对于咱们目前的最次要起因是BeanFactory 中木有getBeansOfType()这个办法~~~

withAnnotation 依据注解查找

IOC 容器还能够依据类上标注的注解来查找对应的 Bean

  1. 创立一个注解类
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface animal {}
  1. 创立几个bean对象
@Animalpublic class Dog {}@Animalpublic class Cat {}public class Xiaoming {}

其中只有Xiaoming这个类没有增加@Animal注解

  1. 批改XML配置文件,增加如下三个申明
<bean id="dog" class="com.huodd.bean.Dog"></bean><bean id="cat" class="com.huodd.bean.Cat"></bean><bean id="xiaoming" class="com.huodd.bean.Xiaoming"></bean>
  1. 批改启动类
public class DlApplication {    public static void main(String[] args) {        ApplicationContext ctx = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml");        Map<String, Object> beans = ctx.getBeansWithAnnotation(Animal.class);        beans.forEach((beanName, bean) -> {            System.out.println(beanName + " : " + bean);        });    }}
  1. 运行main办法 打印后果如下
dog : com.huodd.bean.Dog@313ac989cat : com.huodd.bean.Cat@4562e04d

提早查找

对于一些非凡场景,须要依赖容器中某些特定的bean 然而当他们不存在时如何应用默认/或者缺省策略来解决逻辑呢?

这里咱们先把xml配置文件中的 Dog 的申明临时删掉

这样咱们在获取dog的时候ctx.getBean(Dog.class) 就会报错 NoSuchBeanDefinitionException

  1. 现有计划启用缺省策略
Dog dog;try {    dog = ctx.getBean(Dog.class);} catch (NoSuchBeanDefinitionException e) {    // 找不到Dog时手动创立    dog = new Dog();}System.out.println(dog);

这里咱们把业务代码写在了catch代码块中,不够优雅,性能也有待改善,而且如果前期每个bean都这样解决,代码量微小

  1. 革新下 获取之前查看
 Dog dog = ctx.containsBean("dog") ? (Dog) ctx.getBean("dog") : new Dog();

这里应用到了ApplicationContext中的办法 containsBean 用于查看容器中是否有指定的bean

该办法看似曾经没有问题了,然而要思考到该办法传递的参数只能传递bean的id 不能依照bean的类型去查找 如果bean的名字是其余的呢,工作量还是微小的

  1. 应用提早查找

该机制的大略思路为 当咱们想要获取一个Bean的时候,先返回给咱们一个包装类,等到咱们真正去应用的时候再去“拆包”查看外面到底有没有该Bean对象

应用办法如下

ObjectProvider<Dog> dogProvider = ctx.getBeanProvider(Dog.class);

下面代码能够看到 就是依照后面的思路进行解决的,返回了一个“包装”给咱们,当咱们应用的时候间接调用getObject办法

但如果 容器中没有该Bean 还是会报 NoSuchBeanDefinitionException ,上面会介绍下ObjectProvider提供的其余办法

  • getIfAvailable()该办法能够在找不到Bean的时候返回null 而不抛出异样

    能够应用如下办法实现

    Dog dog = dogProvider.getIfAvailable(Dog::new);
  • ifAvailable()该办法是在取到Bean后马上或者间歇的应用

    代码如下

    dogProvider.ifAvailable(dog -> System.out.println(dog)); // 或者应用办法援用

以上就是对于SpringFramework以及IoC的依赖查找相干内容,小伙伴能够再去顶部查看上面试题,是否都能够了解了并且把握了呢.