Spring入门(一)浅谈Spring
Spring入门(二)快速入门 - IOC/DI
IOC(Inversion of Control):控制反转
控制反转指的是将对象的创建、管理权限从我们程序猿的手里,移交给我们Spring框架。
这时我们不再有创建、管理对象的主动权,改为被动的接收、使用,这种控制权的转换我们称之为控制反转。
主动控制:适合创建简单对象。
控制反转:适合管理创建过程复杂的对象。
DI(Dependency Injection):依赖注入
依赖注入指的是在程序运行期间,由外部容器(Spring)动态的将依赖对象注入到组件中。其实IOC 和 DI 其实是同一个概念。依赖注入是实现控制反转的方式。
目的:解耦
关系:has a
从概念来讲,实际太过于抽象,作为刚刚接触Spring框架的小伙伴们来说,真心不能够理解,实际我也曾经是其中的一员。但是对于操作来讲极其简便。这就是我上文说到的,刚刚学习框架,要做到不求甚解。
举个例子:
我们要完成某样有实际意义的功能,一般来讲都需要两个或两个以上的类组成,并互相之间协作完成的结果。按照传统来讲,我们的每个对象都需要管理和自己互相协作的对象(它所以依赖的对象)的引用,这将导致高度耦合。
而如果我们通过DI,这种对象的依赖关系就由Spring这个框架去进行管理,那我们就实现了松耦合。其次我们现在都是基于接口编程,也是使得其解耦低的原因所在,实现了可替换性。
创建一个 Maven 项目,详情可参考:简单maven项目的创建
导入依赖 (pom.xml)
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.2.RELEASEversion>
dependency>
dependencies>
创建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>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
@Test
public void testIOC() {
// 初始化Spring容器
ApplicationContext ac =
new AnnotationConfigApplicationContext(Config.class);
// 获取被管理的JavaBean
DemoBean bean = ac.getBean(DemoBean.class);
System.out.println(bean);
}
创建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();
}
}
DI(依赖注入) 在上面讲述概念的时候已经提到过了,咱们这里再简单的剖析下:
依赖注入实际来讲分为两部分:
依赖(dependency)
实际依赖我们之前学习 maven 项目的时候就已经接触过这个名词 ,添加依赖 / 导入依赖…
那么什么才是我们的依赖呢?
依赖通俗意义上来讲,是指一个业务功能执行期间所需要的前提:煮米饭必须以米为前提,砍树需要以工具为前提。
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.2.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
dependencies>
注入
注入是指将我们需要的依赖给到 / 传递到使用者,我们称为注入。
实际我们在获取这个依赖的时候可以通过两种方式:主动获取(对象自己创建),和被动得到(对象别人创建,给只管使用即可)。而我们被动得到我们称之为注入。
以煮饭为例:米的生产过程我不需要关注,我只需要去使用即可,这个就是我们被动得到。
以砍树为例:工具的生产过程我不需要关注,我只需要去使用即可,这个就是我们被动得到。
创建一个 Maven 项目,详情可参考:简单maven项目的创建
导入依赖 (pom.xml)
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.2.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
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();
}
}
注解方式进行注入:
修改上述代码:
通过@Component将 Saw 和 Worker 对象交给Spring去管理
通过 @Autowired 注解,将 Saw 对象注入进去
在配置类上添加 @ComponentScan 注解,扫描组件
同样进行测试,得到相同的结果
一般来讲,我们开发中这种方式使用更加简便,也是我们常常使用的方式。
面向接口编程(解耦 / 松耦合)
我们在上述代码上进行修改即可
添加 工具接口 Tool
添加 具体的工具-斧子 Ax ,实现工具接口Tool,并添加@Component注解
修改 具体的工具-锯 Saw ,实现工具接口Tool
修改 Worker类 ,修改注入对象,将注入对象类型改为接口类型
运行测试类:
这时我们的程序会报错,这个是正常的现象,或者说是我们想要出现的效果,这里我们来看下报错信息,分析下报错原因
这里出现了一个异常:NoUniqueBeanDefinitionException 不唯一的bean定义异常
异常错误描述:
没有一个合适的bean类型(cn.cy.bean.Tool)可用,预期有一个单独的匹配的bean,但是找到了 2个 (saw,ax)
这里可以理解为,当我们想把工具注入到Worker对象中时,Spring容器找到了两个Tool工具类型,不知道应该注入哪个类型了,所以会抛出一个 NoUniqueBeanDefinitionException 异常。
解决办法:
这时,我们只需要去指定一下我们我们想要注入的类型即可
那这里需要先了解下@Autowired的注入规则
根据上面的注入规则,有以下两种解决办法:
第一种解决办法:
将具体想要注入的组件,自己定义ID(名字),这样和要注入的名字一致,这样就可以注入进去了,但是这种方式不太常见,我们一般都是保证原生默认的beanID
第二种解决办法:
直接在要注入的地方添加 @Qualifier 注解,告诉 Spring容器通过 指定的beanID(名字)进行注入
如想了解其他异常,可见常见异常汇总:
Bean 的英文意思是 “豆子”
JavaBean 是一种约定 Java 类的定义规范,使得我们程序规范一致,方便使用,交流。它是企业通用的一种编程标准,建议大家都去遵守。
约定:
Spring 同样建议被其管理的对象符合 JavaBean 规范。并且我们通常意义上称呼的 JavaBean对象 就是由 Spring 容器管理的对象,我们也称 Spring 为 Spring容器,IOC容器…
如想了解其他注解 / 注解的具体用法,可见常见异常汇总:
告诉 Spring 在启动时,加载被 @Bean 标注的方法,返回值就是 Spring 创建的对象(交给 Spring 管理的对象)方法名是 Bean 的ID
由 @Configuration 标注的类,告诉 Spring 它是一个配置类
通用组件注解
下面的组件注解是 @Component 注解的衍生注解,主要目的是为了提高标记代码的 “可读性”
控制层组件注解
业务层组件注解
持久层组件注解
@ComponentScan 作用是根据定义的扫描路径,把符合扫描规则的类装配到 Spring 容器中
自动注入注解
与 @Autowired 配合使用,当存在多个实例的时候,指定具体实例的名称
junit 单元测试注解