共计 7528 个字符,预计需要花费 19 分钟才能阅读完成。
谈谈 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 对象
@Animal
public class Dog {
}
@Animal
public 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@313ac989
cat : 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 的依赖查找相干内容, 小伙伴能够再去顶部查看上面试题, 是否都能够了解了并且把握了呢.