Spring两大核心:1.IoC 2.DI
初始IoC & DI
基本概念
在 前面的学习中,我们知道了Spring是一个开源框架,他让我们的开发更加简单,他支持广泛的应用场景,我们用一句更具体的话来概括Spring,那就是:Spring是包含了众多工具的IoC容器
IoC:Inversion of Controller 控制权反转(对象的控制权发生了反转) 我们可以理解为为之前是谁用对象控制权就在谁手里 而现在是由Spring来控制
DI:Dependency Injection:依赖注入
容器在运行期间,动态的为应用程序提供运行时所依赖的资源,称之为依赖注入
Bean:表示对象,IoC容器存的就是Bean对象
我们可以认为:依赖注入(DI)和控制权反转(IoC)是从不同的角度描述同一件事,依赖注入是从应用程序的角度来描述,就是指通过引入IoC容器,.利用依赖关系注入的方式,实现对象之间的解耦
IoC是一种思想,也是"目标",而思想只是一种指导原则,最终还是要有可行的落地方案,而DI就属于具体的实现.所以也可以说,DI是IoC的一种实现
IoC & DI 使用
既然Spring是一个IoC(控制反转)容器,那么他就具备两个最基础的功能:1.存 2.取
Spring容器管理的主要对象,这些对象,我们称之为Bean 我们把这些对象交由Spring管理,由Spring负责对象的创建和销毁 我们程序只需要告诉Spring哪些需要存,以及如何从Spring中取出对象
这里我们要先知道两个注解:1.@Component 表示:把对象交给Spring管理 存
2.@Autowired 表示:从容器中获取对象并给属性赋值 取
IoC详解
IoC控制反转就是将对象的控制权交给Spring的IoC容器,由IoC容器创建以及管理对象 也就是Bean的存储
Bean的存储
两类注解:
1.类注解:@Controller(控制器存储) @Service(服务存储) @Repository(仓库存储) @Component (组件存储)@Configuration(配置存储)
2.方法注解:@Bean
类注解
java
@Controller("aaa")
public class HController {
public void print(){
System.out.println("do Controller");
}
}
如何观察这个对象已经存在Spring容器当中了呢?
接下来我们学习如何从Spring容器中获取对象
java
@SpringBootApplication
public class SpringiocDemoApplication {
public static void main(String[] args) {
//获取Spring上下文对选哪个
ApplicationContext context=SpringApplication.run(SpringiocDemoApplication.class, args);
//从Spring上下文中获取对象
HController bean = (HController) context.getBean("aaa");
//使用对象
bean.print();
}
ApplicationContext 翻译过来就是Spring 上下文 ,因为对象都交给Spring管理了,所以获取对象要从Spring中获取,那么就要先得到Spring的上下文
如果我们删除掉注解@Controller再次运行就会观察到报错信息:no qualifying bean of type
获取bean对象有以下几种方式
1.// 根据bean名称获取bean
Object getBean(String var1) throws BeansException
2.//根据bean名称和类型来获取bean
<T> T getBean(String var1,class<T>var2)throws BeansException
3.//按bean名称和构造函数参数动态创建bean,只使用与具有原型(prototype)作用域的Bean
Object getBean(String var1, Object ... var2)throws BeansException
4.//根据类型获取bean
<T> T getBean(class<T> var1) throws BeansException
5.//按bean类型和构造函数参数动态创建bean,只使用与具有原型(prototype)作用域的Bean
<T> T getBean(class<T> var1,Object ... var2) throws BeansException

常用的是上述1,2,4中,这三种方式,获取到的bean是一样的
其中1,2都涉及到根据名称来获取对象,bean的名称是什么样呢?
Spring bean是Spring框架在运行时管理的对象,Spring会给管理的对象起一个名字
Bean命名约定
程序开发人员不需要为bean指定名称(beanId),如果没有显示的提供名称(beanId),Spring容器将为该bean生成唯一的名称
命名约定成使用Java标准约定作为实例字段名,也就是说bean以小写字母开头,然后使用驼峰式大小写
那么既然获取bean对象有不同的方式,这些不同的方式获取到的bean会是一个对象吗?
java
ApplicationContext context=SpringApplication.run(SpringiocDemoApplication.class, args);
//适合该类型的对象只有一个
HelloController bean = context.getBean(HelloController.class);
bean.print();
HelloController helloController = new HelloController();
helloController.print();
HelloController bean2 = (HelloController) context.getBean("helloController");
bean2.print();
HelloController bean3 = context.getBean("helloController", HelloController.class);
bean3.print();
System.out.println(bean);
System.out.println(bean2);
System.out.println(bean3);
运行结果
地址一样,说明对象是一个
获取bean对象,是父类BeanFactory提供的功能
ApplicationContext vs BeanFactory
继承关系和功能方面来说:Spring容器有两个顶级的接口:BeanFactory和SpringApplication.其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext属于BeanFactory的子类,他除了继承BeanFactory的所有功能以外,他还具有独特的特性,还添加了对国际化支持,资源访问支持以及事件传播等方面的支持
从性能方面来说:ApplicationContext是一次性加载并初始化所有的BeanFactory对象,而BeanFactory是需要那个才去加载那个,因此更加轻量(空间换时间)
剩下的四个类注解的大致用法是一样的,这里我们主要关注这几个类注解的不同





观察这些注解的源代码我们可以发现这些注解除了@Component都有@Component,我们可以理解其他的几个注解是@Component的衍生注解
@Controller:控制层 @Service:业务逻辑层
@Respository:数据层 @Configuration:配置层
@Component:组件层
在实际的应用过程中,这5个注解的边界没有那么清
这5个注解,@Controller不可以和其他注解替换,控制层必须使用@Controller 在不固定的场景下,控制层使用@Service也能访问成功,但不标准,也不稳定,其他注解可以替换,但我们不建议(因为不同注解表示的含义不同)

方法注解@Bean
类注解是添加到某个类上的,但是存在两个问题:1.使用外部包的类,没办法添加类注解
2.一个类,需要多个对象,比如多个数据源
这种情况下,我们需要使用方法注解@Bean
当如果只添加@Bean是无法获取到对象的,在Spring框架的设计中,方法注解@Bean要配合类注解才能将对象正常的存储到Spring容器中
我们也可以定义多个对象,也可以通过设置name属性给Bean对象进行重命名操作
扫描路径
Q:使用学习的注解声明的Bean,一定会生效吗?
A:不一定(原因:bean想要生效,还需要被Spring扫描)
Spring默认的扫描路径:启动类所在的路径(包含子孙目录)
加了@ComponentScan之后,就以这个为主(这个可以配置多个路径(类似数组的定义))
这里我们推荐的做法:把启动类放在我们希望扫描的包的路径下,这样我们定义的bean就可以被扫描到
DI详解
依赖注入是一个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象
在之前的学习中,我们有用@Autowired这个注解,完成了依赖注入的操作
简单来说,就是把对象取出来放到某个类的属性中
关于依赖注入,Spring也给我们提供了三种方式:1.属性注入(Field Injection)
2.构造方法注入(Constructor Injection)
3,Setter注入(Setter Injection)
属性注入
属性注入是使用@Autowired来实现的
@Autowired从Spring容器中根据类型获取对象,并进行赋值 只会是@Autowired下面一行的代码生效
Spring创建对象是依靠反射
java
@Controller
public class HelloConrtoller{
@Autowied
private UserService userService;
public void print(){
System.out.print("HelloController");
userService.print();
}
}
构造方法注入
如果只有一个构造方法时,@Autowired可以省略
如果存在多个构造方法,默认的构造方法是无参的构造方法,也可以通过@Autowired来指定默认的构造方法
java
@Autowired
public HelloController(UserService userService){
this.userService = userService;
}
public HelloController(){
}
Setter方法注入
java
private UserSerivce us;
private UserConfig uc;
@Autowired
public void setUs(UserService us){
this.us = us;
}
@Autowired
public void setUc(UserConfig uc){
this.uc = uc;
}
三种注入优缺点分析
|----|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|
| | 属性注入 | 构造方法注入(Spring4.x推荐) | Setter注入(Spring3.X推荐) |
| 优点 | 简洁,使用方便 | 1.可以注入final修饰的属性 2.注入的对象不会被修改 3.依赖对象在使用前一定会被彻底初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法 4.通用性好,构造方法是JDK支持的,所以更换任何框架,他都是适用的 | 方便在类实例之后,重新对该对象进行配置或者注入 |
| 缺点 | 1.只能用IoC容器,如果是非IoC容器不可用,并且只有在使用的时候才会出现NPE(空指针异常) 2.不能注入一个Final修饰的属性 | 注入多个对象的时候,比较繁琐 | 1.不能注入Final修饰的属性 2.注入对象可能会被改变,因为Setter方法可能会被多次调用,就有被修改的风险 |
@Autowired存在的问题:
当同一个类型存在多个bean时,使用@Autowired会存在问题,会报required a single bean,but 2 were found(非唯一的Bean对象)
java
@Component
public class BeanConfig {
@Bean("u1")
public User user1(){
User u1 = new User();
u1.setName("july");
u1.setAge("12");
return u1;
}
@Bean("u2")
public User use2r(){
User u2 = new User();
u2.setName("nora");
u2.setAge("13");
return u2;
}
}
java
@ResponseBody
@Controller
public class UserController {
@Autowired
private UserService us;
@Autowired
private User user;
public void sayHi(){
us.print();
System.out.println(user);
}
}

如何解决这一问题呢?
Spring提供了一下几种解决方案:
1.@Primary
使用@Primary注解,当存在多个相同类型的Bean注入时,加入@Primary注解 来确定默认的实现
2.@Qualifier
使用@Qualifier注解:指定当前要注入的bean对象.在@Qualifier的value属性中,指定注入的bean名称
@Qualifier注解不能单独使用,必须配合@Autowired使用
3.@Resource
使用@Resource注解:按照bean的名称进行注入,通过name属性指定要注入的bean的名称
依赖注入,通常使用@Autowired和@Resource
参数更加推荐使用@Qualifier
常见的面试题总结
@Autowired和@Resource的区别
1.@Autowired是Spring框架提供的注解,而@Resource是JDK提供的注解
2.@Autowired默认是按照类型注入,而@Resource是按照名称注入 相比于@Autowired来说,@Resource支持更多的参数设置,例如name设置,根据名称获取bean
(这里我们要注意严格来说@Resouce是按照类型+名称来注入)
获取bean对象,是父类BeanFactory的功能
ApplicationContext vs BeanFactory
1.继承关系和功能方面来说:Spring容器有两个顶级的接口:BeanFactory和ApplicationContext.其中BeanFactory提供了基础的访问容器的能力,而ApplicationContext属于BeanFactory的子类,他除了继承了BeanFactory的所有功能之外,他还有独特的特性,还添加了对国际化的支持,资源访问支持,以及事件传播等方面的支持
2.从性能方面来说:ApplicationContext是一次性加载并初始化所有的bean对象(饿汉模式),而BeanFactory是需要哪个才去加载哪个(懒汉模式)
Autowired的装配顺序

Spring,Spring Boot,和Spring MVC的关系和区别
Spring:简单来说,Spring是一个开发应用框架,轻量级,一站式,模块化,其目的是用于简化企业级应用程序开发 Spring主要功能:管理对象,以及对象之间的依赖关系,面向切面编程,数据库事务管理,数据访问,web框架支持等 同时Spring具有高度可开放性,并不强制依赖Spring 比如开发者可以自由选择Spring的部分或者全部,Spring可以无缝继承第三方框架,比如数据访问框架,web框架(这里我们说的是Spring core 不是Spring全家桶)
Spring Boot:Spring Boot是对Spring的一个封装,是为了简化Spring应用的开发而出现的 中小型企业没有成本研究自己的框架,使用SpringBoot可以快速搭建框架降低开发成本,让开发人员更加专注于Spring应用的开发,无需过多关注xml的配置和一些底层的实现
Spring MVC:Spring MVC是Spring的一个子框架,Spring诞生之后,大家觉得好用,于是按照MVC模式设计一个MVC框架(一些用Spring解耦的组件),主要用于web应用和网络接口,所以Spring MVC是一个web框架
总结来说:SpringMVC和SpringBoot都属于Spring SpringMVC是基于Spring的一个MVC框架 而SpringBoot是基于Spring的一套快速开发整合包