目录
1. 什么是Spring
2.什么是IoC容器
3.通过实例来深入了解IoC容器的作用
3.1造一量可以定义车辆轮胎尺寸的车出现的问题
3.2解决方法
3.3IoC优势
4.DI介绍
5.Spring中的IoC和DI的实现
5.1.存对象
5.1.2 类注解
5.1.3 方法注解
5.2取对像 (依赖注入)
5.2.1.属性注入
5.2.2.构造方法注入(Spring4.x推荐的)
5.2.3Setter注入(Sping 3.x推荐)
5.3三种注入方式的优缺点:
5.4@Autowired存在的问题
5.5常见面试题:
1. 什么是Spring
Spring是一个开源框架,他让我们的开发更加简单,它支持广泛的应用场景,有着活跃而庞大的社区,这也是Spring能够长久不衰的原因.这个概念还是相对于比较抽象,我们用通俗易懂的话来讲,Spring是包含了众多工具方法的IoC容器 那么问题来了,容器是什么.什么是IoC容器
2.什么是IoC容器
容器是用来容纳某种物品的装置,IoC是Spring的核心思想,我们在类上面加入@RestControlle和@Controller注解,就是把这个对象交给Spring来管理,Spring框架在启动的时候就会加载该类,把对象交给Spring来管理就是IoC思想
IoC: inversion of Con\rol(控制反转) 也就是说Spring是一个"控制反转容器"
那么什么是控制反转呢?也就是控制劝的反转.即获取对象的过程被反转了.
也就是说,当需要某个对象的时候,传统开发模式需要我们在类里面自己new对象,现在不需要我们自己去创建,而是把创建对象的任务交给容器,程序只需要依赖注入就可以了,这个容器被称为IoC容器,Spring是一个IoC容器,所以Spring有时候也被称为Spring容器.
控制反转是一种思想,在生活中也随处可见,比如,自动驾驶,在传统驾驶模式中,我们对于车的控制权是司机,但是在自动驾驶的时候,我们对于车辆的控制权就交给了自动驾驶系统.这也是一种控制反转
3.通过实例来深入了解IoC容器的作用
3.1造一量可以定义车辆轮胎尺寸的车出现的问题
我们想造一辆车,但是造车的时候,需要车身,造车身又需要底盘,底盘需要轮胎,我们当我造一辆车的时候,可以这样造:
public class Car {
private Framework framework; //我们在创建车的时候需要一个车身 这时候还需要一个车身类
public Car(){
framework = new Framework();
System.out.println("Car init....");
}
public void run(){
System.out.println("Car run ....");
}
}
// 车身
public class Framework {
private Bottom bottom;
public Framework(){
bottom = new Bottom();
System.out.println("Framework init...");
}
}
//底盘
public class Bottom {
private Tire tire; //轮胎
public Bottom(){
System.out.println("Bottom init ...");
}
}
//轮胎
public class Tire {
int size; //轮胎的尺寸
public Tire(){
System.out.println("车胎的尺寸是");
}
}
我们没写轮胎的尺寸的时候,相安无事,看起来很好,但是如果我们在轮胎的构造方法中加入它的尺寸.
就会出问题了,我们要想在造车的时候就传入轮胎的大小,就得把这一系列的代码构造方法全部改了,每一个都得加入int类型的size参数,这样无异于是很麻烦的.
那么有没有一种简单一些的办法可以让我们优化这些步骤呢?
3.2解决方法
我们尝试换一种方法,我们先设计汽车的大概样子,然后根据汽车来设计车身,在根据车身来设计地盘,在根据底盘来设计轮胎.
package com.example.demo.Car;
public class Car {
private Framework framework; //我们在创建车的时候需要一个车身 这时候还需要一个车身类
public Car(Framework framework){
this.framework = framework;
System.out.println("Car init....");
}
public void run(){
System.out.println("Car run ....");
}
public static void main(String[] args) {
Tire tire = new Tire(14);
Bottom bottom = new Bottom(tire);
Framework framework1 = new Framework(bottom);
Car car = new Car(framework1);
car.run();
// 我们先造一个轮胎,然后轮胎指定了尺寸,
//把轮胎给底盘,底盘给车身,车身给车 这样我们如果想改车胎的参数,
//只需要改车胎的构造方法就行了,不需要把其它的都一起改
}
}
// 车身
public class Framework {
private Bottom bottom; //底盘
public Framework(Bottom bottom){
this.bottom = bottom;
System.out.println("Framework init...");
}
}
//底盘
public class Bottom {
private Tire tire; //轮胎
public Bottom(Tire tire){
this.tire = tire;
System.out.println("Bottom init ...");
}
}
//轮胎
public class Tire {
int size; //轮胎的尺寸
public Tire(int size){
System.out.println("车胎的尺寸是"+size);
}
}
3.3IoC优势
传统的代码对象创建的顺序是 car-framework - bottom - tire
改进之后解耦代码的对象创建顺序是 tire - bottom - framework - car
我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是Car控制并创建了
Framework,Framework创建并创建了Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再
是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由
当前类控制了.
这样的话,即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是IoC的实现思想。
这样IoC容器有以下好处
1.资源不由资源双方管理,而是不使用资源的第三方来进行管理,资源集中管理,实现了资源的可配置和易管理
2 降低了使用资源双方的依赖程度,也就是我们常说的解耦合.
4.DI介绍
上面学习了IoC,那么什么是DI呢? DI:Dependency Injection(依赖注⼊)
容器在运行阶段,动态的为应用程序提供运行的时候所依赖的资源,称为依赖注入.
程序运行的时候,需要哪个资源,此时容器就提供这个资源. 依赖注入和控制反转是从不同的角度来描述同一件事,通过引入IoC容器,利用依赖关系注入的方式,实现对象间的解耦合.
IoC是一种思想,也就是一种指导原则,而DI就是具体的实现,也就是说DI是IoC是一种实现
5.Spring中的IoC和DI的实现
既然是容器,那么肯定有存和取,Spring针对这两种操作都有自己的实现,下面我们来介绍一下在spring中存放对象和取对象这两种操作分别应该怎么用
5.1.存对象
共有两种注解类型可以实现:
5.1.2 类注解
@Controller (控制器存储)
@Controller
public class TestController {
public void func(){
System.out.println("TestController...");
}
}
该类注解存放的是控制器对象,可以和前端访问或者交互到
那么Spring把这个对象存到哪里了呢? 我们可以在Spring给的main函数中验证出来
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//Spring上下文 存放的是Spring容器管理的对象
ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
//通过类名和类型来找到这个对象
TestController controller = context.getBean("testController", TestController.class);
//执行对象中的方法
controller.func();
}
}
@Serivce (服务存储) 存储的是业务逻辑相关的对象
@Repository 仓库存储 存储数据相关的 也称为吃就吃
@Component (组件存储) 其它几个类存储注解都继承自这个
@Configuration 配置层 处理项目中的一些配置信息
其实这些注解⾥⾯都有⼀个注解 @Component ,说明它们本⾝就是属于 @Component 的"⼦类".
@Component 是⼀个元注解,也就是说可以注解其他类注解,如 @Controller , @Service ,
@Repository 等.这些注解被称为 @Component 的衍⽣注解.
5.1.3 方法注解
类注解固然可以用,但是存在两个问题:
1.使用外部包里的类,没办法添加类注解
2.一个类需要多个对象
我们可以使用我们的@Bean 方法注解 在Spring中 方法注解要配合我们的类注解才能存储到Spring容器中,如下:
@Component
public class BeanConfig {
@Bean
public User user1() {
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
}
上述代码中,我们定义了多个对象.那么这些对象我们又该怎么去取呢?
我们尝试获取一下
public static void main(String[] args) {
//Spring上下文 存放的是Spring容器管理的对象
ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
//通过类名和类型来找到这个对象
User user = context.getBean(User.class);
//执行对象中的方法
System.out.println(user);
}
运行以后发现代码报错了.
我们可以根据名字来获取到对象
public static void main(String[] args) {
//Spring上下文 存放的是Spring容器管理的对象
ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
//通过类名和类型来找到这个对象
User user = context.getBean("user1",User.class);
//执行对象中的方法
System.out.println(user);
}
我们还可以通过@Bean注解重命名对象
5.2取对像 (依赖注入)
依赖注入是一个过程,是在IoC容器创建Bean时,去提供运行的时候所需要以来的资源,而这个资源就是对象.我们可以提供@Autowried这个注解来完成依赖注入这个操作
关于依赖注入,Spring也给我们提供了三种方法,
1. 属性注⼊(Field Injection)
2. 构造⽅法注⼊(Constructor Injection)
3. Setter注⼊(Setter Injection)
5.2.1.属性注入
我们可以举个例子来说明一下:
@Controller
public class TestController {
@Autowired
private UserService userService;
public void func(){
userService.func();
}
}
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
//Spring上下文 存放的是Spring容器管理的对象
ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
//通过类名和类型来找到这个对象
TestController testController = context.getBean(TestController.class);
//执行对象中的方法
testController.func();
}
}
可以看到我们通过了依赖注入,拿到了对象,并且把它的方法使用了出来.
5.2.2.构造方法注入(Spring4.x推荐的)
这种是在构造方法中实现注入的
如果只要一个构造方法 那么@Autowried可以省略,如果有多个就需要添加@Autowried来明确指定到底要使用哪个构造方法.
5.2.3Setter注入(Sping 3.x推荐)
Setter注⼊和属性的Setter法实现类似,只不过在设置set⽅法的时候需要加上@Autowired注
解,如下代码所⽰
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
5.3三种注入方式的优缺点:
1.属性注入:
优点:简洁,使用方便,
缺点:只能用于IoC容器,并且会出现NPE
不能注入一个fina修饰的属性
2.构造函数注入;
优点: 可以注入fina修饰的属性,
注入的对象不会被修改
通用性好,构造方法是JDK支持的,换了任何框架都支持
依赖对象在使用时就会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就执行的方法
缺点:注入多个对象的时候,代码繁琐
3.Setter注入
优点:方便在类实例之后,重新对改对象进行配置或者注入
缺点:不能注入final修饰的属性.注入对象夸你汇编,因为setter方法可能会被多次调用,有被修改的风险
5.4@Autowired存在的问题
同一个类型存在多个bean时 @Autowired会存在问题
@Autowired
private User user;
public void func(){
System.out.println(user);
}
会报这个错误,
那么我们该如何解决这个问题呢?
Spring给我们 提供了三种解决方案:
1.@Primary注解
使用该注解,指定默认的对象
@Component
public class BeanConfig{
@Bean
@Primary
public User user1() {
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
@Bean
public User user2() {
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
}
重新运行一下:
2.@Qualifier注解
指定当前要注入的bean对象
@Autowired
@Qualifier("user2")
private User user;
public void func(){
System.out.println(user);
}
3.Resource注解,按照bean名称进行注入,通过name属性指定要注入bean的名称
@Resource(name = "user2")
private User user;
public void func(){
System.out.println(user);
}
5.5常见面试题:
@Autowird与@Resource的区别
1.@Autowird是spring框架提供的注解,而@Resource是JDK提供的
2.@Autowird是按照类型注入,@Resource是按照bean名称注入.相比于@Autowired来说,@Resource支持更多的参数设置,入name的设置,根据名称获取bean