谈谈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
可通过如下几点进行形容:
- IOC 实现了组件之间的解耦
- AOP 切面编程将利用业务做对立或特定的性能加强, 可实现利用业务与加强逻辑的解耦
- 容器治理利用中应用的Bean、托管Bean的生命周期、事件与监听的驱动机制
- 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 的初始化之后 ) - 配置元信息(
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重要版本的更新工夫及次要个性
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 |
3. IOC依赖查找
根底框架搭建
- 创立Maven模块,这里以
ioc-learning
为例 - 引入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version></dependency>
- 创立配置文件
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>
- 申明一般类
Person.java
public class Person {}
ioc-learning-dl.xml
配置文件退出Persion
的申明
<bean id="person" class="com.huodd.bean.Person"></bean>
- 创立启动类
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); }}
- 运行打印
com.huodd.bean.Person@57baeedf
胜利打印出 Person
的全限定类名 + 内存地址,证实编写胜利。
3.1 byName 名称查找
上述根底框架中的步骤6
外围代码
Person person = (Person) factory.getBean("person");
3.2 byType 类型查找
1. 一般类
- 批改配置文件
ioc-learning-dl.xml
将person
的申明中id
属性去掉
<bean class="com.huodd.bean.Person"></bean>
- 批改启动类
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
类型 返回值也无需再进行强转
- 运行
main
办法 打印如下
com.huodd.bean.Person@61862a7f
2. 接口
- 创立接口
demoDao
以及 实现类DemoDaoImpl
public interface DemoDao { List<String> findAll();}public class DemoDaoImpl implements DemoDao{ @Override public List<String> findAll() { return Arrays.asList("user1", "user2", "user3"); }}
- 批改配置文件
ioc-learning-dl.xml
退出DemoDaoImpl
的申明
<bean class="com.huodd.dao.DemoDaoImpl"></bean>
- 批改启动类
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()); }
- 运行
main
办法 打印后果如下
com.huodd.dao.DemoDaoImpl@7334aada[user1, user2, user3]
由此可见 DemoDaoImpl
注入胜利 并且BeanFactory
能够依据接口类型找到对应的实现类
3.3 高级查找
ofType 依据类型查找多个
如果一个接口有多个实现类,如何一次性的把所有的实现类都取出来呢? 后面用到的getBean
办法显然无奈满足 需应用到ofType
办法
- 继下面的代码 创立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"); }}
- 批改配置文件
ioc-learning-dl.xml
退出新建的两个实现类的申明
<bean class="com.huodd.dao.impl.DemoMysqlDaoImpl"></bean> <bean class="com.huodd.dao.impl.DemoOracleDaoImpl"></bean>
- 批改启动类
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.beans
和 org.springframework.context
包是 SpringFramework 的 IOC 容器的根底。BeanFactory
接口提供了一种高级配置机制,可能治理任何类型的对象。ApplicationContext
是 BeanFactory
的子接口。它减少了:
- 与 SpringFramework 的 AOP 性能轻松集成
- 音讯资源解决(用于国际化)
- 事件公布
- 应用层特定的上下文,例如 Web 应用程序中应用的
WebApplicationContext
如此说来 ApplicationContext
蕴含了 BeanFactory
的所有性能,并且还扩大了更多的个性
其实对于咱们目前的最次要起因是BeanFactory
中木有getBeansOfType()
这个办法~~~
withAnnotation 依据注解查找
IOC 容器还能够依据类上标注的注解来查找对应的 Bean
- 创立一个注解类
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface animal {}
- 创立几个bean对象
@Animalpublic class Dog {}@Animalpublic class Cat {}public class Xiaoming {}
其中只有Xiaoming
这个类没有增加@Animal
注解
- 批改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>
- 批改启动类
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); }); }}
- 运行
main
办法 打印后果如下
dog : com.huodd.bean.Dog@313ac989cat : com.huodd.bean.Cat@4562e04d
提早查找
对于一些非凡场景,须要依赖容器中某些特定的bean 然而当他们不存在时如何应用默认/或者缺省策略来解决逻辑呢?
这里咱们先把xml配置文件中的 Dog
的申明临时删掉
这样咱们在获取dog的时候ctx.getBean(Dog.class)
就会报错 NoSuchBeanDefinitionException
- 现有计划启用缺省策略
Dog dog;try { dog = ctx.getBean(Dog.class);} catch (NoSuchBeanDefinitionException e) { // 找不到Dog时手动创立 dog = new Dog();}System.out.println(dog);
这里咱们把业务代码写在了catch代码块中,不够优雅,性能也有待改善,而且如果前期每个bean都这样解决,代码量微小
- 革新下 获取之前查看
Dog dog = ctx.containsBean("dog") ? (Dog) ctx.getBean("dog") : new Dog();
这里应用到了ApplicationContext
中的办法 containsBean
用于查看容器中是否有指定的bean
该办法看似曾经没有问题了,然而要思考到该办法传递的参数只能传递bean的id 不能依照bean的类型去查找 如果bean的名字是其余的呢,工作量还是微小的
- 应用提早查找
该机制的大略思路为 当咱们想要获取一个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的依赖查找相干内容,小伙伴能够再去顶部查看上面试题,是否都能够了解了并且把握了呢.