谈谈SpringFramework与IOC依赖查找

开发 后端
SpringFramework 是一个开源的、松耦合的、分层的、可配置的一站式企业级 Java 开发框架,它的核心是 IOC 与 AOP ,它可以更容易的构建出企业级 Java 应用,并且它可以根据应用开发的组件需要,整合对应的技术。

[[375995]]

 1. 面试题

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

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

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

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

2. 为何使用SpringFramework

可通过如下几点进行描述:

IOC 实现了组件之间的解耦

AOP 切面编程将应用业务做统一或特定的功能增强, 可实现应用业务与增强逻辑的解耦

容器管理应用中使用的Bean、托管Bean的生命周期、事件与监听的驱动机制

Web、事务控制、测试、与其他技术的整合

3. SpringFramework包含哪些模块?

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

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

5. BeanFactory与ApplicationContext的对比

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

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

ApplicationContext 主要扩展了以下功能:

  • AOP 的支持( AnnotationAwareAspectJAutoProxyCreator 作用于 Bean 的初始化之后 )
  • 配置元信息( BeanDefinition 、Environment 、注解等 )
  • 资源管理( Resource 抽象 )
  • 事件驱动机制( ApplicationEvent 、ApplicationListener )
  • 消息与国际化( 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重要版本的更新时间及主要特性

3. IOC依赖查找

基础框架搭建

1.创建Maven模块,这里以ioc-learning为例

2.引入依赖

  1. <dependency> 
  2.     <groupId>org.springframework</groupId> 
  3.     <artifactId>spring-context</artifactId> 
  4.     <version>5.2.8.RELEASE</version> 
  5. </dependency> 

3.创建配置文件 ioc-learning-dl.xml

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2.    <beans xmlns="http://www.springframework.org/schema/beans" 
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans 
  5.            https://www.springframework.org/schema/beans/spring-beans.xsd"> 
  6.     
  7.    </beans> 

4.声明普通类Person.java

  1. public class Person { 

5.ioc-learning-dl.xml配置文件加入Persion的声明

  1. <bean id="person" class="com.huodd.bean.Person"></bean> 

6.创建启动类

  1. public class DlApplication { 
  2.     public static void main(String[] args) { 
  3.         // 读取配置文件  使用接口 BeanFactory 接收  
  4.         BeanFactory factory = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml"); 
  5.         // 通过配置文件中声明的 id 进行对象的获取 
  6.         Person person = (Person) factory.getBean("person"); 
  7.         System.out.println(person); 
  8.     } 

7.运行打印

  1. com.huodd.bean.Person@57baeedf 

成功打印出 Person 的全限定类名 + 内存地址,证明编写成功。

3.1 byName 名称查找

上述基础框架中的步骤6

核心代码

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

3.2 byType 类型查找

1. 普通类

1.修改配置文件 ioc-learning-dl.xml 将person的声明中id属性去掉

  1. <bean class="com.huodd.bean.Person"></bean> 

2.修改启动类

  1. public static void main(String[] args) { 
  2.         BeanFactory factory = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml"); 
  3. //        Person person = (Person) factory.getBean("person"); 
  4.         Person person = factory.getBean(Person.class); 
  5.         System.out.println(person); 
  6.     } 

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

3.运行main方法 打印如下

  1. com.huodd.bean.Person@61862a7f 

2. 接口

1.创建接口demoDao 以及 实现类 DemoDaoImpl

  1. public interface DemoDao { 
  2.     List<String> findAll(); 
  3.  
  4. public class DemoDaoImpl implements DemoDao{ 
  5.     @Override 
  6.     public List<String> findAll() { 
  7.         return Arrays.asList("user1""user2""user3"); 
  8.     } 

2.修改配置文件 ioc-learning-dl.xml 加入 DemoDaoImpl的声明

  1. <bean class="com.huodd.dao.DemoDaoImpl"></bean> 

3.修改启动类

  1. public static void main(String[] args) { 
  2.        BeanFactory factory = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml"); 
  3.        DemoDao demoDao = factory.getBean(DemoDaoImpl.class); 
  4.        System.out.println(demoDao); 
  5.        System.out.println(demoDao.findAll()); 
  6.    } 

4.运行main方法 打印结果如下

  1. com.huodd.dao.DemoDaoImpl@7334aada 
  2. [user1, user2, user3] 

由此可见 DemoDaoImpl 注入成功 并且BeanFactory可以根据接口类型找到对应的实现类

3.3 高级查找

ofType 根据类型查找多个

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

1.继上面的代码 创建2个DemoDao的实现类 如下

  1. public class DemoMysqlDaoImpl implements DemoDao { 
  2.     @Override 
  3.     public List<String> findAll() { 
  4.         return Arrays.asList("mysql_user1""mysql_user2""mysql_user3"); 
  5.     } 
  6. public class DemoOracleDaoImpl implements DemoDao { 
  7.     @Override 
  8.     public List<String> findAll() { 
  9.         return Arrays.asList("oracle_user1""oracle_user2""oracle_user3"); 
  10.     } 

2.修改配置文件 ioc-learning-dl.xml 加入新建的两个实现类的声明

  1. <bean class="com.huodd.dao.impl.DemoMysqlDaoImpl"></bean> 
  2. <bean class="com.huodd.dao.impl.DemoOracleDaoImpl"></bean> 

3.修改启动类

  1. public static void main(String[] args) { 
  2.         ApplicationContext ctx = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml"); 
  3.         Map<String, DemoDao> beans = ctx.getBeansOfType(DemoDao.class); 
  4.         beans.forEach((beanName, bean) -> { 
  5.             System.out.println(beanName + " : " + bean.toString()); 
  6.         }); 
  7.  
  8.     } 

运行main方法 打印结果如下

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

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

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

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

org.springframework.beans 和 org.springframework.context 包是 SpringFramework 的 IOC 容器的基础。BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。ApplicationContext 是 BeanFactory 的子接口。它增加了:

  • 与 SpringFramework 的 AOP 功能轻松集成
  • 消息资源处理(用于国际化)
  • 事件发布
  • 应用层特定的上下文,例如 Web 应用程序中使用的 WebApplicationContext

如此说来 ApplicationContext 包含了 **BeanFactory 的所有功能,**并且还扩展了更多的特性

其实对于我们目前的最主要原因是BeanFactory 中木有getBeansOfType()这个方法~~~

withAnnotation 根据注解查找

IOC 容器还可以根据类上标注的注解来查找对应的 Bean

1.创建一个注解类

  1. @Documented 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Target(ElementType.TYPE) 
  4. public @interface animal { 

2.创建几个bean对象

  1. @Animal 
  2. public class Dog { 
  3.  
  4. @Animal 
  5. public class Cat { 
  6.  
  7. public class Xiaoming { 

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

3.修改XML配置文件,添加如下三个声明

  1. <bean id="dog" class="com.huodd.bean.Dog"></bean> 
  2. <bean id="cat" class="com.huodd.bean.Cat"></bean> 
  3. <bean id="xiaoming" class="com.huodd.bean.Xiaoming"></bean> 

4.修改启动类

  1. public class DlApplication { 
  2.     public static void main(String[] args) { 
  3.         ApplicationContext ctx = new ClassPathXmlApplicationContext("dl/ioc-learning-dl.xml"); 
  4.         Map<String, Object> beans = ctx.getBeansWithAnnotation(Animal.class); 
  5.         beans.forEach((beanName, bean) -> { 
  6.             System.out.println(beanName + " : " + bean); 
  7.         }); 
  8.     } 

5.运行main方法 打印结果如下

  1. dog : com.huodd.bean.Dog@313ac989 
  2. cat : com.huodd.bean.Cat@4562e04d 

延迟查找

对于一些特殊场景,需要依赖容器中某些特定的bean 但是当他们不存在时如何使用默认/或者缺省策略来处理逻辑呢?

这里我们先把xml配置文件中的 Dog 的声明暂时删掉

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

NoSuchBeanDefinitionException

1.现有方案启用缺省策略

  1. Dog dog; 
  2. try { 
  3.     dog = ctx.getBean(Dog.class); 
  4. } catch (NoSuchBeanDefinitionException e) { 
  5.     // 找不到Dog时手动创建 
  6.     dog = new Dog(); 
  7. System.out.println(dog); 

这里我们把业务代码写在了catch代码块中,不够优雅,性能也有待改善,而且如果后期每个bean都这样处理,代码量巨大

2.改造下 获取之前检查

  1. Dog dog = ctx.containsBean("dog") ? (Dog) ctx.getBean("dog") : new Dog(); 

这里使用到了ApplicationContext中的方法 containsBean 用于检查容器中是否有指定的bean

该方法看似已经没有问题了,但是要考虑到该方法传递的参数只能传递bean的id 不能按照bean的类型去查找 如果bean的名字是其他的呢,工作量还是巨大的

3.使用延迟查找

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

使用方法如下

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

上面代码可以看到 就是按照前面的思路进行处理的,返回了一个“包装”给我们,当我们使用的时候直接调用getObject方法

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

  • getIfAvailable()该方法可以在找不到Bean的时候返回null 而不抛出异常

可以使用如下方法实现

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

代码如下

  1. dogProvider.ifAvailable(dog -> System.out.println(dog)); // 或者使用方法引用 

以上就是关于SpringFramework以及IoC的依赖查找相关内容,小伙伴可以再去顶部查看下面试题,是否都可以理解了并且掌握了呢? 

 

责任编辑:姜华 来源: PoXing
相关推荐

2021-05-06 07:58:57

Spring BeanIOCAOP

2020-08-06 00:14:16

Spring IoC依赖注入开发

2020-08-17 07:59:47

IoC DINestJS

2013-07-05 14:47:51

IoC需求

2013-09-02 17:53:41

MVC架构设计MEF

2011-03-29 09:51:58

GuiceIOC

2021-01-22 06:35:44

IoCxml驱动技术

2024-03-28 10:37:44

IoC依赖注入依赖查找

2012-02-02 13:04:50

JavaSpring

2022-07-01 09:39:58

SpringAOPIOC

2017-10-13 10:36:33

SparkSpark-Strea关系

2012-05-29 21:38:14

Metro UI

2022-05-20 07:59:35

数据库数字基础设施

2011-11-08 09:46:10

2010-03-30 09:04:26

Silverlight依赖属性附加属性

2015-07-13 09:45:32

阿里校招

2020-07-01 07:44:06

javaSE==equals

2021-10-18 07:43:30

RedisAOF日志RDB快照

2021-11-29 12:11:09

npm包管理器工具

2016-10-11 15:42:08

点赞
收藏

51CTO技术栈公众号