乐趣区

关于程序员:谈谈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 当前)
    • *
  1. 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.x

jdk 1.3

基于 xml 的配置

SpringFramework 2.x

jdk 1.4

改进 xml 文件、初步反对注解式配置

SpringFramework 3.x

Java 5

注解式配置、JavaConfig 编程式配置、Environment 形象

SpringFramework 4.x

Java 6

SpringBoot 1.x、外围容器加强、条件拆卸、WebMvc 基于 Servlet3.0

SpringFramework 5.x

Java 8

SpringBoot 2.x、响应式编程、SpringWebFlux、反对 Kotlin

  1. 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 对象
@Animal
public class Dog {
}

@Animal
public 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@313ac989
cat : 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 的依赖查找相干内容, 小伙伴能够再去顶部查看上面试题, 是否都能够了解了并且把握了呢.

退出移动版