1.初识Spring
Spring 是包含了众多⼯具方法的 IoC 容器(控制反转容器)
容器: 容器是用来容纳某种物品的基本装置 IoC: Inversion of Control 翻译成中⽂是 “控制反转” 的意思 Spring 让控制权发生了反转, 不再是上级对象创建并控制下级对象了, 而是把下级对象注入到当前对象中, 下级的控制权不再由上级类控制了, 这样即使下级类发生任何改变, 当前类都是不受影响的, 这就是典型的控制反转, 也就是 IoC 的实现思想
1.1.SpringIoC容器的核心功能
Spring 是⼀个 IoC 容器, 说的就是将对象的创建和销毁的权利都交给 Spring 来管理了, 它本身又具备了存储对象和获取对象的能力
既然Spring 是⼀个 IoC 容器, 那容器就会拥有两个最基础的功能 将Bean(对象)存储到Spring(容器)中 将Bean(对象)从Spring(容器)中取出来 所以学 Spring 最核心的功能, 就是学如何将对象存⼊到 Spring 中, 再从 Spring 中获取对象的过 程
1.2.DI 和 IoC
说到 IoC 不得不提的⼀个词就是 “DI”
DI 是 Dependency Injection 的缩写, 翻译成中⽂是 “依赖注入” 的意思所谓依赖注入, 就是由 IoC 容器在运行期间, 动态地将某种依赖关系注⼊到对象中(这里控制权就发生了反转). 所以依赖注⼊(DI)和控制反转(IoC)是从不同的角度描述的同⼀件事情
IoC和DI的区别
2.Spring的创建和使用
图示Spring的创建和使用
2.1.创建Spring项目
使⽤ Maven ⽅式来创建⼀个 Spring 项⽬分为3步
创建⼀个普通 Maven 项⽬ 添加 Spring 框架支持(spring-context、spring-beans) 添加启动类
2.1.1.创建普通Maven项目
首先选择New --> Project 新建一个Maven项目.
选择Maven 后 点击Next 即可.不需要勾选任何添加.
选择项目要创建的目录,并更改项目名称
2.1.2.添加 Spring 框架支持
在pom.xml中添加依赖并刷新依赖.等待右下角进度条下载完成即可.
下面是我导入的依赖.大家也可以在中央仓库中搜索依赖并添加.
org.springframework spring-context 5.2.3.RELEASE org.springframework spring-beans 5.2.3.RELEASE
2.1.3.添加启动类
在src->main->java 目录下.创建启动类 名称可以自定义.
在启动类中添加main方法即可.
这样,我们的Spring项目就创建好了.
2.2.存储Bean对象
所谓的 Bean 就是 Java 语⾔中的⼀个普通对象.
存储 Bean 分为以下 2 步
存储 Bean 之前, 先要有 Bean 才可以, 因此先要创建⼀个 Bean 将创建的 Bean 注册到 Spring 容器中
2.2.1.创建Bean
首先在java目录下创建一个文件夹用于存放我们的Bean.在文件夹下创建一个User对象.
给User类添加一个方法.这样我们就创建好了用于展示的Bean.
User类代码.
package com.beans;/*** name User* @deprecated exercises:* 普通类,普通Bean对象*/
public class User {public void sayHi(String name) {System.out.println("Hello: " + name);}
}
2.2.2.创建xml配置文件
首先在 resources 目录下创建配置文件.可以自定义名称(这里使用spring.xml) 添加xml模板.这样配置文件就创建好了.模板直接复制即可.
2.2.3.将 Bean 注册到容器中
在刚创建的配置文件中,添加 bean 标签,即可将Bean注册到容器中. 存放方式类似于Map id是存放入容器中的key值,用来获取Bean对象 class是存放对象的包名+类名
2.3.获取并使用 Bean 对象
获取并使用 Bean 对象, 分为以下 3 步
得到 Spring 上下文对象, 因为对象都交给 Spring 管理了, 所以获取对象要从 Spring 中获取, 那么就要先得到 Spring 的上下文 两种获取方式,注意参数要和配置文件名称相同 ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“spring.xml”); BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(“spring.xml”)); 通过 Spring 上下文,获取某⼀个指定的 Bean 对象 获取有三种方式 通过id获取Bean对象 通过类型获取Bean对象 该方法的好处是直接确定了类型, 不需要强转 缺点是如果Spring容器中注册了多个此类型对象时, Spring容器会不知道你获取的对象是谁, 从而会报错 通过id+类型获取Bean对象 不需要强转, 也不会查询到多个, 因为注册到Spring容器中对象的id是唯一的 缺点是书写比较麻烦 使用 Bean 对象 如果取多个 Bean 的话重复以上第 2、3 步骤
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;public class App {public static void main(String[] args) {//获取上下文对象,两种方式 都可以用来获取Bean对象//1.得到Spring上下文对象(参数要和配置文件名称相同)ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");//2.bean工厂BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring.xml"));//根据上下文对象提供的方法获取到bean 获取有三种方式//1.根据id获取bean 方法的参数要和xml中的id对应,方法返回Object类型,需要强转.User user1 = (User) applicationContext.getBean("user");//使用user1.sayHi("echo");//2.根据类型获取bean对象,只适用于Spring容器中只注册了一个此类型对象//该方式的好处是 不需要强转//(当Spring中存在多个同一个类型对象不适用)User user2 = applicationContext.getBean(User.class);user2.sayHi("echo");//3.根据类型+id 获取bean对象//不需要强转,也不会查询到多个,因为注册到Spring容器中对象的id是唯一的.User user3 = applicationContext.getBean("user", User.class);user3.sayHi("echo");//根据beanFactory来获取对象.三种方式同样适用User user4 = (User) beanFactory.getBean("user");user4.sayHi("XX");User user5 = beanFactory.getBean(User.class);user5.sayHi("XX");User user6 = beanFactory.getBean("user", User.class);user6.sayHi("XX");}
}
2.3.1.ApplicationContext VS BeanFactory
相同点: 都可以实现从容器中获取Bean,都提供了getBean方法
不同点:
ApplicationContext 属于 BeanFactory的子类 BeanFactory 只提供了基础访问Bean的方法 ApplicationContext 除了拥有 BeanFactory 的所有功能之外,还提供了更多的方法实现 比如对国际化的支持、资源访问的支持、以及事件和传播等方面的支持 从性能方面来说二者是不同 BeanFactory 是按需加载 Bean,更轻量 ApplicationContext 是饿汉方式, 在创建时会将所有的Bean都加载起来,以备以后使用
3.Spring 更简单的存储和读取对象 – 注解
3.1.配置扫描路径
要使用注解的方式, 我们首先要 配置扫描路径 让Spring启动时扫描 配置(com.beans)包 中的注解.只有被配置的包下的所有类, 添加注解才能被正确的识别并保存到 Spring 中.
在我们之前的配置文件 “spring.xml” 中配置扫描路径(这里配置的 com.beans Spring启动时就会扫描这个包)
3.2.更简单的存储对象(认识注解)
五大注解(类注解) @Controller @Service @Repoistory @Configuration @Component 方法注解
五大注解是在类之前添加的注解, 添加之后可以让这个类注册到Spring容器中.而方法注解则是要在方法前添加的注解, 可以将方法的返回值注册到Spring容器中.要注意的是方法注解一定要配合五大注解来使用.
3.2.1.五大注解的使用
五大注解的功能是相同的, 都是将类注册到Spring容器中. 既然功能是相同的, 那为什么还要有五大注解? 原因就是可以使程序员通过注解来了解类的作用.
@Controller :控制器 @Service :服务 @Repoistory :仓库 @Configuration :配置 @Component :组件
如图,保证这个类所在的包在注册的包范围内.并且在类前添加了五大注解.这个类就被注册到Spring中了
添加到Spring容器中之后, 我们就可以通过之前的方法来获取和使用Bean对象了.
五大注解的命名规则
五大注解的命名规则是根据 JDK Introspector 中的 decapitalize 方法 来执行的.(图中代码为源码)
根据源码,我们可以知道命名规则:
如果名称长度大于1, 并且名称中第一个字符和第二个字符都是大写, 就返回原类名. 否则就将类名中的第一个字符从大写转变为小写返回.
知道了五大注解的命名规则. 我们在获取通过五大注解注册的Bean对象时就不会发生不知道名称的问题了.
五大注解之间的关系
观察 @Controller / @Service / @Repository / @Configuration 注解的源码就会发现. 这四个注解都是 @Component 注解的子类
3.2.2.方法注解 @Bean
类注解是添加到某个类上的, 而方法注解是放到某个方法上的. 但是要注意的是 方法注解是要配合类注解来使用的. 如果只写了方法注解,没有在类上添加注解. Spring在启动的时候是不会扫描到的.(可以理解为方法注解只会缩小扫描范围,不会扩大扫描范围)
同样: 添加好方法注解和类注解之后, 方法的返回值就被添加到Spring容器中. 我们就可以之前的方法来获取对象.
方法注解的使用方式
直接在方法上添加@Bean注解 使用@Bean注解, 并添加name属性用来重命名 -> @Bean(name = “name”) 设置名称可以设置单个,也可以通过 {} 设置多个 -> @Bean(name = {“name1”, “name2”})
@Bean 的命名规则
方法注解的命名规则和五大注解的命名规则大有不同.
当没有设置name属性时, 那么Bean默认的名称就是方法名 当设置了name属性之后,就只能通过重命名的name属性对应的值来获取 重命名之后,无法再使用方法名获取bean对象
3.3.更简单的获取对象(对象注入)
3.3.1.对象注入有两种方式
@Autowired 注解 @Resource 注解
两种注解实现的效果是相同的 直接使用注解的时候,会根据类型来寻找对象. 如果可以找到的对象只有一个, 就会直接注入成功. 如果找到的对象有多个,就会继续根据名称来寻找. 如果有名称相同的就注入,没有就会报错.
在不能更改对象名称的时候 可以使用@Resource注解的name属性设置对象名称 @Autowired没有此属性.所以需要@Qualifier注解配合 @Qualifier只有value一个属性,所以可以省略
这样在根据类型查询到多个对象时,就会属性设置的新名称来查询
@Resource vS @Autowired
既然两种注解实现的效果是相同的,那这两个注解有什么不同呢?
出身不同 @Autowired是Spring框架提供的 @Resource 来自于JDK (Java亲儿子) 用法不同 @Autowired 支持属性注入、构造方法注入和Setter注入 @Resource 不支持构造方法注入 支持的参数不同 @Autowired 只支持required 这一个参数 @Resource 支持更多的参数设置,比如 name、type 设置
3.3.2.根据注解完成对象装配(对象注入)的实现方法有以下 3 种
属性注入 直接在属性上添加 @Autowired注解 或 @Resource注解即可 构造方法注入 在构造方法上添加 @Autowired注解 @Resource注解不支持构造方法注入 Setter 注入 在Setter方法上添加 @Autowired注解 或 @Resource注解即可
4.Bean的作用域和生命周期
4.1.Bean的作用域
Bean的作用域类型有六种,注意后 4 种状态是 Spring MVC 中的值, 普通的 Spring 项目中只有前两种
singleton: 单例模式(Bean默认作用域类型) prototype: 原型模式(多例模式) request: 请求作用域(存在于Spring MVC中) 范围是一次请求 session: 会话作用域(存在于Spring MVC中) 范围是一个会话 application: 全局作用域(存在于Spring MVC中) 范围是全局 websocket: HTTP WebSocket 作用域(存在于Spring WebSocket中)
Spring中可以使用 @Scope注解 来更改Bean的作用域类型( Spring中的Bean默认作用域类型是单例 )
使用单词 @Scope(“prototype”) : 多例 @Scope(“singleton”) : 单例 使用枚举类 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) : 多例 @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) : 单例
单例作用域(singleton)VS全局作用域(application)
singleton是Spring Core的作用域, application是 Spring Web中的作用域 singleton 作用于loC的容器, 而 application 作用于Servlet容器.
4.2.Bean的生命周期
实例化(给bean分配内存空间) 设置属性(对象注入) 初始化(在设置属性之后,避免使用属性时属性为空) 执行各种通知(执行各种Aware) 执行 BeanPostProcessor 初始化前置方法 执行 @PostConstruct 初始化方法,依赖注⼊操作之后被执行 执行自己指定的 init-method 方法 执行 BeanPostProcessor 初始化后置方法 使用Bean 销毁Bean @PreDestroy 重写DisposableBean接口方法 destroy-method
先设置属性再进行初始化的原因
要先设置属性是因为如果先初始化, 初始化的时候就可能需要使用属性.这个时候属性为null, 就会空指针异常.