Spring系列文章目录
Spring入门(一)浅谈Spring
Spring入门(二)快速入门 – IOC/DI
- Spring系列持续更新中…
前言
一、Spring核心之 IOC 、DI
1. IOC
IOC(Inversion of Control):控制反转
控制反转指的是将对象的创建、管理权限从我们程序猿的手里,移交给我们Spring框架。
这时我们不再有创建、管理对象的主动权,改为被动的接收、使用,这种控制权的转换我们称之为控制反转。
主动控制:适合创建简单对象。
控制反转:适合管理创建过程复杂的对象。
2. DI
DI(Dependency Injection):依赖注入
依赖注入指的是在程序运行期间,由外部容器(Spring)动态的将依赖对象注入到组件中。其实IOC 和 DI 其实是同一个概念。依赖注入是实现控制反转的方式。
目的:解耦
关系:has a
从概念来讲,实际太过于抽象,作为刚刚接触Spring框架的小伙伴们来说,真心不能够理解,实际我也曾经是其中的一员。但是对于操作来讲极其简便。这就是我上文说到的,刚刚学习框架,要做到不求甚解。
举个例子:
我们要完成某样有实际意义的功能,一般来讲都需要两个或两个以上的类组成,并互相之间协作完成的结果。按照传统来讲,我们的每个对象都需要管理和自己互相协作的对象(它所以依赖的对象)的引用,这将导致高度耦合。
而如果我们通过DI,这种对象的依赖关系就由Spring这个框架去进行管理,那我们就实现了松耦合。其次我们现在都是基于接口编程,也是使得其解耦低的原因所在,实现了可替换性。
面向接口编程(解耦 / 松耦合)
二、第一个Spring项目
1. IOC – 代码实现
1.1 第一种方式:@Configuration + @Bean
项目结构:
-
创建一个 Maven 项目,详情可参考:简单maven项目的创建
-
导入依赖 (pom.xml)
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.2.RELEASE</version> </dependency> </dependencies>
pom.xml 文件 如图所示:
-
创建JavaBean – DemoBean
/** * @Author lss */ public class DemoBean { }
-
创建配置类
/** * @Author lss */ @Configuration public class Config { @Bean public DemoBean getDemoBean() { return new DemoBean(); } }
-
测试 DemoBean 对象是否交给Spring去管理
/** * @Author lss */ public class IOCTest { public static void main(String[] args) { // 初始化Spring容器 ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); // 获取被管理的JavaBean DemoBean bean = ac.getBean(DemoBean.class); System.out.println(bean); } }
当然这里如果已经对于Juint 单元测试 有些了解的情况下,我们测试也可以使用 Juint,能达到同样的效果
在 pom.xml 文件中 导入 Juint 单元测试依赖
<!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency>
pom.xml 文件 如图所示:
@Test public void testIOC() { // 初始化Spring容器 ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); // 获取被管理的JavaBean DemoBean bean = ac.getBean(DemoBean.class); System.out.println(bean); }
如图所示:
运行输出结果为:
1.2 第二种方式:@Component + @ComponentScan 组件扫描
-
创建JavaBean – ExampleBean,类上添加 @Component 注解
/** * @Author lss */ @Component public class ExampleBean { }
-
在配置类上,添加 @ComponentScan 注解
/** * @Author tedu-sysh-lrf */ @Configuration @ComponentScan("cn.cy.bean") public class Config { @Bean public DemoBean getDemoBean() { return new DemoBean(); } }
-
测试
运行输出结果为:
2. DI – 代码实现
DI(依赖注入) 在上面讲述概念的时候已经提到过了,咱们这里再简单的剖析下:
依赖注入实际来讲分为两部分:
-
依赖(dependency)
实际依赖我们之前学习 maven 项目的时候就已经接触过这个名词 ,添加依赖 / 导入依赖…
那么什么才是我们的依赖呢?
依赖通俗意义上来讲,是指一个业务功能执行期间所需要的前提:煮米饭必须以米为前提,砍树需要以工具为前提。<dependencies> <!-- spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.2.RELEASE</version> </dependency> <!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> </dependencies>
-
注入
注入是指将我们需要的依赖给到 / 传递到使用者,我们称为注入。
实际我们在获取这个依赖的时候可以通过两种方式:主动获取(对象自己创建),和被动得到(对象别人创建,给只管使用即可)。而我们被动得到我们称之为注入。
以煮饭为例:米的生产过程我不需要关注,我只需要去使用即可,这个就是我们被动得到。
以砍树为例:工具的生产过程我不需要关注,我只需要去使用即可,这个就是我们被动得到。
我们这里还是以刚才的项目进行使用测试。
-
创建一个 Maven 项目,详情可参考:简单maven项目的创建
-
导入依赖 (pom.xml)
<dependencies> <!-- spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.2.RELEASE</version> </dependency> <!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> </dependencies>
-
创建JavaBean – Saw / Worker
Saw/** * @Author lss */ public class Saw implements Serializable { private String name = "电锯"; @Override public String toString() { return name; } }
Worker
/** * @Author lss */ public class Worker implements Serializable { private String name = "光头强"; public Saw saw; public void work() { System.out.println(name +"使用" + saw + "砍树"); } }
-
创建配置类
/** * @Author lss */ @Configuration public class Config { @Bean public Saw saw() { return new Saw(); } @Bean public Worker worker(Saw s) { Worker worker = new Worker(); worker.saw = s; return worker; } }
-
创建测试类,测试,Saw对象是否被注入到 Worker对象中
/** * @Author tlss */ public class DITests { @Test public void testDI() { ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); Worker bean = ac.getBean("worker",Worker.class); bean.work(); } }
运行输出结果为:
2.1 Spring IOC组件注入时组件自动匹配规则
- 首先按照注入参数类型查找响应类型的Bean组件,如果没有则直接报错
- 如果在Spring容器中能够匹配上唯一类型的Bean组件,则进行注入成功
- 如果按照类型匹配到两个或两个以上的bean组件,则在查找组件ID和变量名是否匹配,如果匹配则注入成功
- 如果组件类型和组件ID都不能很好的匹配则报错
2.2 @Autowired
注解方式进行注入:
修改上述代码:
通过@Component将 Saw 和 Worker 对象交给Spring去管理
通过 @Autowired 注解,将 Saw 对象注入进去
在配置类上添加 @ComponentScan 注解,扫描组件
同样进行测试,得到相同的结果
一般来讲,我们开发中这种方式使用更加简便,也是我们常常使用的方式。
2.3 接口解耦
面向接口编程(解耦 / 松耦合)
我们在上述代码上进行修改即可
添加 工具接口 Tool
添加 具体的工具-斧子 Ax ,实现工具接口Tool,并添加@Component注解
修改 具体的工具-锯 Saw ,实现工具接口Tool
修改 Worker类 ,修改注入对象,将注入对象类型改为接口类型
运行测试类:
这时我们的程序会报错,这个是正常的现象,或者说是我们想要出现的效果,这里我们来看下报错信息,分析下报错原因
这里出现了一个异常:NoUniqueBeanDefinitionException 不唯一的bean定义异常
异常错误描述:
没有一个合适的bean类型(cn.cy.bean.Tool)可用,预期有一个单独的匹配的bean,但是找到了 2个 (saw,ax)
这里可以理解为,当我们想把工具注入到Worker对象中时,Spring容器找到了两个Tool工具类型,不知道应该注入哪个类型了,所以会抛出一个 NoUniqueBeanDefinitionException 异常。
解决办法:
这时,我们只需要去指定一下我们我们想要注入的类型即可
那这里需要先了解下@Autowired的注入规则
2.3 @Autowired的注入规则
- 首先按照类型进行注入(byType),如果没有则直接报错
- 如果在 Spring容器 中能够匹配到唯一类型的 Bean 组件,则注入成功
- 如果按照类型匹配到两个或两个以上的组件,则再查找组件ID和变量名是否匹配(byType),如果匹配则注入成功
- 如果组件类型和组件ID都不能很好的匹配则报错
根据上面的注入规则,有以下两种解决办法:
- 利用组件自定义ID,当组件ID设置为 tool 时, @Autowired 注解会根据ID匹配成功
- 利用注解 @Qualifier(“beanID”)指定注入bean组件的ID
第一种解决办法:
将具体想要注入的组件,自己定义ID(名字),这样和要注入的名字一致,这样就可以注入进去了,但是这种方式不太常见,我们一般都是保证原生默认的beanID
第二种解决办法:
直接在要注入的地方添加 @Qualifier 注解,告诉 Spring容器通过 指定的beanID(名字)进行注入
如想了解其他异常,可见常见异常汇总:
- 常见异常汇总链接:xxx(更新中…)
3. 专有名词
3.1 Java Bean
Bean 的英文意思是 “豆子”
JavaBean 是一种约定 Java 类的定义规范,使得我们程序规范一致,方便使用,交流。它是企业通用的一种编程标准,建议大家都去遵守。
约定:
- 需要定义包 package
- 有无参的构造方法(无参构造器)
- 需要实现序列化接口
- 类中声明 getXxx 和 setXxx 方法(getter ,setter 方法)
Spring 同样建议被其管理的对象符合 JavaBean 规范。并且我们通常意义上称呼的 JavaBean对象 就是由 Spring 容器管理的对象,我们也称 Spring 为 Spring容器,IOC容器…
4. 注解
如想了解其他注解 / 注解的具体用法,可见常见异常汇总:
- 常见注解汇总链接:xxx(更新中…)
4.1 @Bean
告诉 Spring 在启动时,加载被 @Bean 标注的方法,返回值就是 Spring 创建的对象(交给 Spring 管理的对象)方法名是 Bean 的ID
4.2 @Configuration
由 @Configuration 标注的类,告诉 Spring 它是一个配置类
4.3 @Component
通用组件注解
下面的组件注解是 @Component 注解的衍生注解,主要目的是为了提高标记代码的 “可读性”
4.3.1 @Controller
控制层组件注解
4.3.2 @Service
业务层组件注解
3.3.3 @Repository
持久层组件注解
4.4 @ComponentScan
@ComponentScan 作用是根据定义的扫描路径,把符合扫描规则的类装配到 Spring 容器中
4.5 @Autowired
自动注入注解
4.6 @Qualifier
与 @Autowired 配合使用,当存在多个实例的时候,指定具体实例的名称
4.7 @Test
junit 单元测试注解
总结
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/107664.html