一句话描述Spring :
Spring包含了众多工具方法的IOC容器
一 、IOC的理解
IOC:是一个控制反转的容器 :
那什么是控制反转:
就是在我们创建的一个A类中, 如果要使用B类按照传统的做法,我们需要在A类中new一个B类的对象, 从而使用B类中的方法.这样在如果我们修改了B类中的一些属性, 在A类中new的时候也需要进行相应的修改,程序的耦合程度( 相关性) 比较高.一旦要修改,就需要修改很多的代码
控制反转就是我们要在A类中使用 B类的方法, 不在A类中创建B类对象, 而是将 创建对象的过程交给 Spring 来完成,放入IOC容器中, 在我们需要B类的这个对象的时候,直接去Spring的IOC容器中取出来使用即可. 这样就实现了代码之间的解耦, 使得代码之间的相关性大大降低
DI 依赖注入:
就是为IOC容器中的Spring所管理的对象和属性进行赋值 , 交给Spring所管理的对象称为bean
二、创建一个Spring项目
1)创建一个Maven项目
2)导入 Spring相关的依赖 Spring-context 和 Spring-Bean;
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
3)创建main方法,用于启动
4)创建相关类进行使用Spring
public class User {
public static void sayHi() {
System.out.println("SayHi");
}
}
5)配置Spring的配置文件Spring-config.xml;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
</beans>
6)相关类存放到Spring中,进行管理
在spring配置文件中使用<bean id = “自定义名称”, class = “类的路径”
<bean id="user" class="com.ykh.User"/>
注意点:通常 id 设置为 类名的小驼峰方式
7)使用
主类中 ApplicationContext context = new ClassPathXmlApplicationContext(“spring-config.xml”);先获取Spring容器
在通过 context.getBean(“配置文件中自定义的bean名称”)获取bean对象, 此处返回Object型对象,需要进行强转.
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); //参数为 spring配置文件的名称
//获取bean对象的3种方法 :
// 1.使用 id 唯一标识获取
User bean1 = (User)context.getBean("user");
System.out.println(bean1);
// 2.通过全类名获取 ( 最常用)
// (注意: 1. 根据类型获取Bea时,要求IOC容器中有且只有一个类型匹配的bean, 如果有多个会 抛异常 NoUniqueBeanDefinitionException
// 2. 若一个都没有 , 抛异常: NoSuchBeanDefinitionException
User bean2 = context.getBean(User.class);
System.out.println(bean2);
//3 .根据bean 的 id 和 类型来获取
User bean3 = context.getBean("user",User.class);
System.out.println(bean3);
}
}
三、基于注解管理bean
通过上面的方法可以获取到bean对象, 但是还是过于麻烦, 我们每创建一个类要想将其通过Spring进行管理,都需要进行在 配置文件中 添加一个bean.
Spring中为我们提供了一种更加方便的方式去 将类 交给Spring管理
<context:component-scan base-package="com.ykh"/>
在 Spring-config.xml 中写上如下语句 , 用于扫描 base-package = 后面的路径下的所有类。
那我们如何让Spring知道,我们要将哪些类交给Spring管理呢?
我们可以使用两类注解:
(一) 类注解管理bean
将要托管给Spring的类上加上类注解 :
@Controller (控制器)
@Service (服务)
@Repository (仓库)
@Component (组件)
@Configuration (配置)
结论: @Controller (控制器) \ @Service (服务) @Repository (仓库) @Configuration (配置) 都是基于@Component 实现的
加入其中一个即可, 具体哪个根据你这个类的作用进行选择
此时运行程序, 就会自动扫描 对应的路径 下的类, 将被注解标记的类,交给Spring管理, 简化了写bean的过程.
使用:
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); //参数为 spring配置文件的名称
//获取bean对象的3种方法 :
// 1.使用 id 唯一标识获取
User bean1 = (User)context.getBean("user");
}
}
使用扫描的方式添加bean, 其每个类对应的id 默认是 该类名的小驼峰写法
(二)方法注解@bean
类注解是作用在类上,将类交给Spring管理, 我们可以使用@Bean注解将方法同样交给Spring管理, 只需要方法上加上@Bean注解即可。
注意: Spring在实例化容器中管理的Bean时, 是通过无参构造来创建对象的, 加入Spring容器中的类必须要有无参构造. 否则在创建的时候就会报错.如下图
四、引入bean的三种方式
(一)属性注入
使用@Autowired注解,得到的是一个单例的对象
@Autowired
UserService userService;
优点: 写法简单
缺点:
-
不能注入一个final 修饰的属性
原因: java语法规定 : final 修饰的变量需满足两种场景,
在使用时直接赋值
在构造方法中直接赋值
-
通用性问题: 只适用于IOC框架(容器),代码的可移植性不高, 代码放到其他框架中就无法使用
-
设计原则问题: 更容易违背单一设计原则.(因为使用简单,所以滥用的风险更大)
(二)setter注入
写法:
将@Autowired注解加在setter方法上
@Autowird
UserService uerService;
public void setterUserService(UserService uerService){
this.userService = userService;
}
优点: 符合单一设计的原则( 一个Setter只针对一个对象)
缺点:
- 不能注入一个不可变对象 ( 无法使用final修饰该变量
- 注入对象可能被改变
(三)构造器注入
UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
注意:
- 当使用构造器注入时, 如果只有一个构造方法,可以不用在构造方法上加@Autowired注解
- 加了@Autowired的构造中的类必须都在spring容器中存在,否则会报错
- 一个类中可以有多个构造方法, 但只能有一个使用了@Autowired注解的构造方法.
优点:
- 可以注入一个不可变的对象( final修饰的对象)
原因: 根据java语法的规定, final修饰的变量在必须在定义的时候进行赋值,或者在构造方法中进行赋值, 此时我们使用构造方法进行注入,符合java的语法规范
- 注入的对象不可以被修改.
原因: 构造方法只会在创建的时候执行一次
- 依赖对象在使用前一定会被完全初始化.
原因: 依赖是在类的构造方法中执行的,而 构造方法是类创建之初就会执行的方法.
- 通用性更好
原因: 因为构造方法是java支持的,所以换到其他任何框架都适用**
缺点:
如果一次性进行多个对象的注入, 会不符合单一设计的原则
(四) 使用Resource注解注入bean
在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如下:
-
属性注入
@Resource UserService userService;
-
Setter注入
UserService userService; @Resource public void setUserService(UserService userService) { this.userService = userService; }
-
目前Resource注解并不支持构造器注入
因为构造器是在类加载的时候就进行的, 而Resource是源于JDK的,所以,在使用构造器的时候,Resource可能还没有被加载
Resource 和 Autowired的区别
- 查找Bean的顺序上的区别
Resource : 实现先根据我们定义的对象名去Spring容器中进行查找,再根据对象的类型查找
Autowired: 先根据类名查找,再根据对象名查找
-
其中所具有的方法的区别
Autowired中只有一个Require方法,默认为true
Resource中则有多个方法,可适应多种不同的场景
解决同⼀个类型,多个 bean 的解决⽅案有以下两个:
- 使⽤ @Resource(name=“user1”) 定义。
- 使⽤ @Qualifier 注解定义名称。
五、Bean的作用域与生命周期
(一)作用域
Bean的作用域指的是Bean在Spring容器中的一种行为模式, Bean的作用域共有以下六种
- singleton: 单例作用域
- prototype: 原型作用域
- request: 请求作用域
- session: 会话作用域
- application: 全局作用域
- websocket: HTTP Websocket作用域
注意: 后四种是SpringMVC中的作用域, 普通的Spring项目只有前两种
1. Singleton作用域
描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀ 个对象。( 单例模式)
场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新
备注: Spring中默认使用该作用域
2. prototype: 原型作用域
描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的 对象实例。 (多例模式)
场景:通常有状态的Bean使⽤该作⽤域
3. request: 请求作用域
描述:每次http请求会创建新的Bean实例,类似于prototype
场景:⼀次http的请求和响应的共享Bean
备注:限定SpringMVC中使⽤
4. session: 会话作用域
描述:在⼀个http servlet Context中,定义⼀个Bean实例
场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
备注:限定SpringMVC中使⽤
5. application 全局作用域
描述:在⼀个http servlet Context中,定义⼀个Bean实例
场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
备注:限定SpringMVC中使⽤
6. websocket : HTTP Websocket作用域
描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息 头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。
备注:限定Spring WebSocket中使⽤
单例作用域(singleton) 和 全局作用域 (application) 的区别
- singleton 是 SpringCore中的作用域, application 是SpringWeb 中的作用域
- singletons作用于IOC容器, 而application作用于Servlet容器
7. Bean的作用域设置
可以通过@Scope注解 , 写在你要改变作用域的Bean的位置
第一种写法: (这种IDEA会提示,不容易出错)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Repository
public class UserRepositoryImpl {
public User getUser() {
return new User(1,"张三");
}
}
第二种写法:
@Scope("prototype")
@Repository
public class UserRepositoryImpl {
public User getUser() {
return new User(1,"张三");
}
}
(二)Bean的生命周期
Bean 的生命周期分为以下 5 部分:
1.实例化 Bean(为 Bean 分配内存空间)
2.设置属性(Bean 注入和装配)
3.Bean 初始化
- 实现了各种 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;
- 执行 BeanPostProcessor 初始化前置方法;
- 执行 @PostConstruct 初始化方法,依赖注入操作之后被执行;
- 执行自己指定的 init-method 方法(如果有指定的话);
- 执行 BeanPostProcessor 初始化后置方法。
4.使用 Bean
5.销毁 Bean