目录
一.IoC
通过这篇博客 Spring Web MVC-优快云博客 我们已经了解Spring是一个开源框架,他可以让我们开发更加简单。
用具体的话概括Spring的话,就是:Spring是包含了众多工具方法的IoC容器
1.1什么是IoC?
loC是Spring的核心思想
其实loC我们在前面已经使用了,在类上面添加@RestController和@Controller注解,就是把这个对象交给Spring管理,Spring 框架启动时就会加载该类.把对象交给Spring管理,就是loC思想.
IoC: Inversion of Control (控制反转) 也就是说 Spring 是⼀个"控制反转"的容器
什么是控制反转呢?也就是控制权反转.什么的控制权发生了反转?获得依赖对象的过程被反转了也就是说,当需要某个对象时,传统开发模式中需要自已通过new创建对象,现在不需要再进行创建,把创建对象的任务交给容器,程序中只需要依赖注入(DependencyInjection,Dl)就可以了,这个容器称为:loC容器。Spring是一个loC容器,所以有时Spring也称为Spring容器.控制反转是一种思想,生活中处处体现
例如:⾃动驾驶, 传统驾驶⽅式, ⻋辆的横向和纵向驾驶控制权由驾驶员来控制, 现在交给了驾驶⾃ 动化系统来控制, 这也是控制反转思想在⽣活中的实现
举个案例:
需求:造一辆车
传统开发的实现思路:
先设计轮子(Tire),然后根据轮子的大小设计底盘(Bottom),接着根据底盘设计车身(Framework),最后根据车身设计好整个汽车(Car)。这里就出现了一个"依赖"关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子.
问题是:当最底层代码改动之后,整个调用链上的所有代码都需要修改. (耦合高)
IoC开发实现思路:
改进之后的控制权发生的反转,不再是使用方对象创建并控制依赖对象了,而是把依赖对象注入将当前对象中,依赖对象的控制权不再由当前类控制了.优势在于 耦合低,这样的话,即使依赖类发生任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是loC的实现思想。从⽽实现了更加灵活、通⽤的程序设计了
1.2 loC容器具备以下优点:
资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。
- 资源集中管理:loC容器会帮我们管理一些资源(对象等),我们需要使用时,只需要从loC容器中去取就可以了
我们在创建实例的时候不需要了解其中的细节,降低了使用资源双方的依赖程度,也就是耦合度。
二.DI
DI:(依赖注入)
容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入。
loC是一种思想,也是"目标",而思想只是一种指导原则,最终还是要有可行的落地方案,而DI就属于具体的实现。所以也可以说,DI是loC的一种实现
既然Spring是一个loC(控制反转)容器,作为容器,那么它就具备两个最基础的功能:
- 存
- 取
Spring 容器管理的主要是对象,这些对象,我们称之为"Bean".
三.Bean的储存(五大注解)
共有两类注解类型实现:
- 类注解:@Controller @Service @Component @Repository @Configuration ( 注意:如果类名前两个字符大写,则bean的名称是类名本身)
- 方法注解:@Bean (Bean名就是方法名)
3.1 @Controller(控制器存储)
使用@Controller存储 bean的代码如下 :
@Controller //将对象存储到Spring中 public class UController { public void sayHi(){ System.out.println(" hi UController"); } }
如何判断是否存到容器当中了呢
下面尝试从Spring容器中获取对象 (共有三种方式):
读取 bean 的代码: @SpringBootApplication
@SpringBootApplication public class SpringIocDemoApplication { public static void main(String[] args) { //获取spring上下文对象 ApplicationContext run = SpringApplication.run(SpringIocDemoApplication.class, args); /* * 常用获取bean的方法(三种) * 输出三种方式,结果证明地址一样,对象一样 * */ //方式一:根据类型,从Spring上下⽂中获取对象 userController bean = run.getBean(userController.class); bean.sayHi(); System.out.println(bean); //方式二:根据bean名,从Spring上下⽂中获取对象 userController bean1 = (userController)run.getBean("userController"); bean1.sayHi(); System.out.println(bean1); //方式三://根据bean类型+名称, 从Spring上下⽂中获取对象 userController bean2 = run.getBean("userController", userController.class); bean2.sayHi(); System.out.println(bean2); } }
![]()
3.2 @Service(服务存储)
使⽤ @Service 存储 bean 的代码如下所⽰:@Service public class UserService { public void sayHi(){ System.out.println("Hi UserService"); } }
从Spring容器中获取对象public static void main(String[] args) { //获取spring上下文对象 ApplicationContext run = SpringApplication.run(SpringIocDemoApplication.class, args); //方式一:根据类型,从Spring上下⽂中获取对象 UserService userService = run.getBean(UserService.class); userService.sayHi(); System.out.println(userService); }
3.3 @Repository(仓库存储)
使⽤ @Repository 存储 bean 的代码如下所⽰:@Repository public class UserRepository { public void sayHi(){ System.out.println("Hi UserRepository"); } }
从Spring容器中获取对象
public static void main(String[] args) { //获取spring上下文对象 ApplicationContext run = SpringApplication.run(SpringIocDemoApplication.class, args); //方式一:根据类型,从Spring上下⽂中获取对象 UserRepository userRepository = run.getBean(UserRepository.class); userRepository.sayHi(); System.out.println(userRepository); }
3.4 @Component (组件存储)
使⽤ @Component存储 bean 的代码如下所⽰:
@Component public class ComPonent { public void sayHi(){ System.out.println("Hi ComPonent"); } }
从Spring容器中获取对象:
public static void main(String[] args) { //获取spring上下文对象 ApplicationContext run = SpringApplication.run(SpringIocDemoApplication.class, args); //方式一:根据类型,从Spring上下⽂中获取对象 UserComPonent UserComPonent = run.getBean(UserComPonent.class); UserComPonent.sayHi(); System.out.println(UserComPonent); }
3.5 @Configuration(配置存储)
使⽤ @Configuration存储 bean 的代码如下所⽰:
@Configurable public class UserConfiguration { public void sayHi(){ System.out.println("Hi UserConfiguration"); } }
从Spring容器中获取对象
public static void main(String[] args) { //获取spring上下文对象 ApplicationContext run = SpringApplication.run(SpringIocDemoApplication.class, args); //方式一:根据类型,从Spring上下⽂中获取对象 UserConfiguration userConfiguration = run.getBean(UserConfiguration.class); userConfiguration.sayHi(); System.out.println(userConfiguration); }
3.6为什么要这么多类注解?
这个也是和咱们前面讲的应用分层是呼应的.让程序员看到类注解之后,就能直接了解当前类的用途。
- @Controller:控制层,接受请求,对请求进行处理,并进行响应
- @Servie:业务逻辑层,处理具体的业务逻辑
- @Repository:数据访问层,也称为持久层,负责数据访问操作
- @Configuration:配置层,处理项目中的一些配置信息 (这四个类全是@Component的衍生类,功能实现上,除了@Controller类其他是可以混用的(不推荐))
3.6.1方法注解 @Bean
类注解是添加到某个类上的,但是存在俩个问题:
- 使用外部包里的类,没办法添加类注解
- 一个类中,需要多个对象,
以上两种场合,我们就需要使用方法注解@Bean 必须搭配五大类使用
注意:
只有在五大注解下@bean才会生效 , 查看启动类所在的目录及其子孙目录
在获取对象时 ,发现有两个或多个时,可以采用bean名+类名,来获取对象
小总结:
程序被Spring管理,需要具备以下条件:
1.被Spring扫描到(默认扫描路径是 启动类所在的目录,包含子目录)
2.需要配置五大注解和@Bean使用
四.DI 详解
依赖注入是一个过程,是指IoC容器在创建Bean时,去提供运行是所以来的资源,而资源指的就是对象,需要使用@Autowired这个注解,来完成依赖注入。
简单来说,就是把对象取出来放到某个类的属性中
Spring提供了三种方式进行依赖注入:
- 属性注入(Field Injection)
- 构造方法注入 (Constructor Injection)
- Setter注入 (Setter Injection)
4.1属性注入
演示 属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中
注意:无法注入一个final修饰的属性
Service 代码如下:
@Service public class UserService { public void sayHi(){ System.out.println("Hi UserService"); } }
Controller 代码如下:
@Controller //将对象存储到spring中 public class UserController { //注入方法一:屬性注入 @Autowired public UserService userService; public void sayHi(){ System.out.println("Hi UserController"); } }
获取 Controller 中的 sayHi⽅法:![]()
4.1构造方法注入
- 如果类中只有一个构造方法,@Autowired注解可以省略,
- 如果类中有多个构造方法,那么需要@Autowired来指定到底使用哪个构造方法
- 构造函数规范: 如果添加构造函数,把无参构造函数显示添加
@Controller //将对象存储到spring中
public class UserController {
//注入方法二:构造方法注入
public UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("Hi UserController");
userService.sayHi();
}
}
4.2 Setter方法注入
Setter注入和属性的Setter方法实现类似,只不过在设置set方法的时候需要加上@Autowired注解
@Controller //将对象存储到spring中 public class UserController { //注入方法三:Setter方法注入 public UserService userService; public UserController(UserService userService) { this.userService = userService; } @Autowired public void setUserService(UserService userService) { this.userService = userService; } public void sayHi(){ System.out.println("Hi UserController"); userService.sayHi(); } }
当程序中同一个类型有多个对象时,使用@AutoWired会报错(一些情况下) 解决方法:
Spring提供了以下⼏种解决⽅案:
- @Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现
- @Qualifier注解:指定当前要注入的bean对象。在@Qualifier的value属性中,指定注入的bean的名称。@Qualifier注解不能单独使⽤,必须配合@Autowired使⽤
- 使用@Resource注解:是按照bean的名称进行注入。通过name属性指定要注入的bean的名称。
解决思想:指定Bean的名称
指定Bean
@Autowird与@Resource的区别
4.3三种注⼊优缺点分析:
Autowired装配顺序 :