[框架学习 | 第三部分] Spring 上篇(Spring 入门、核心功能、Spring Bean -> 定义、范围、生命周期、依赖注入) - 3.
3.1Bean定义
简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象。
我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。
3.1.1元数据—>Bean标签的属性
- 在IOC容器创建Bean对象时候,需要知道这三个信息
- 如何创建一个bean(class)
- bean的生命周期
- bean的依赖关系
所有配置元数据都转换为一组构成每个 bean 标签定义的以下属性:
序号 | 属性 | 描述 |
---|---|---|
1 | class | 指定了用于创建 bean 的 bean类型 |
2 | id或者name | 唯一地指定 bean 标识符 |
3 | scope | 指定从特定 bean 定义创建的对象的范围 |
4 | constructor-arg | 用于注入依赖关系 |
5 | properties | 用于注入依赖关系 |
6 | autowiring mode | 用于注入依赖关系 |
7 | lazy-initialization mode | 延迟初始化的 bean,即 告诉 IoC 容器在第一次被请求时创建一个 bean 实例,而不是在启动时 |
8 | initialization method | 在容器设置了 bean 上的所有必要属性之后调用的回调 |
9 | destruction method | 包含 bean 的容器被销毁时要使用的回调 |
3.1.2配置的方式
Spring IoC 容器与实际写入此配置元数据的格式完全分离。 以下是向 Spring 容器提供配置元数据的三个重要方法 −
- 基于 XML 的配置文件。(Spring)
- 基于注解的配置(SpringBoot)
- 基于 Java 的配置
(1)XML方式
<bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
<property name = "message" value = "Hello World!"/>
</bean>
(2)注解方式
-
@Component
:通用的注解,可标注任意类为Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。 -
@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。 -
@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。 -
@Controller
: 对应 Spring MVC 控制层,主要用于接受用户请求并调用Service
层返回数据给前端页面。
3.1.3@Component和@Bean的区别?
-
相同:两者都是用来声明一个Bean类,交给IOC容器处理
-
不同:
- 作用的地方不同:@Component 注解作用于类,而==@Bean注解作用于方法==。
-
产生的作用不同:
-
@Component
通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用@ComponentScan
注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。 -
@Bean
注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean
告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。
-
-
自定义强度不同:
-
@Bean
注解比@Component
注解的自定义性更强 - 很多地方我们只能通过
@Bean
注解来注册 bean。比如当我们引用第三方库中的类需要装配到Spring
容器时,则只能通过@Bean
来实现
-
(1)@Bean使用示例
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
上面的代码相当于下面的 xml 配置:
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
3.2Bean作用域
3.2.1作用域分类
- singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
-
prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续
getBean()
两次,得到的是不同的 Bean 实例。 - request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
- session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
- application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
- websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
3.2.2配置作用域的方式
(1)xml方式
<bean id="..." class="..." scope="singleton"></bean>
(2)注解方式
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
return new Person();
}
3.3Bean生命周期
对于普通的 Java 对象来说,它们的生命周期就是:
- 实例化
- 该对象不再被使用时通过垃圾回收机制进行回收
对于 Spring Bean 的生命周期来说:
- 实例化bean
- bean属性赋值
-
初始化bean
- 执行处理aware接口,分别调用 setBeanName()、setBeanFactory()、 setApplicationContext()方法
- 调用BeanPostProcessor的预初始化方法
- 调用InitializingBean的afterPropertiesSet()方法
- 调用init-method属性指定初始化方法
- 调用BeanPostProcessor的初始化后方法
- 使用bean
-
销毁bean
- 调用DisposableBean的destroy方法
- 调用destroy-method属性指定的方法
3.3.1初始化回调
(1)InitializingBean的afterPropertiesSet()方法
org.springframework.beans.factory.InitializingBean 接口指定单个方法
void afterPropertiesSet() throws Exception;
可以简单地实现上述接口,并且可以在 afterPropertiesSet() 方法中完成初始化工作,如下所示 −
public class ExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
public class ExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
(2)init-method属性指定初始化方法
基于 XML 的配置元数据的情况下,您可以使用 init-method 属性来指定具有 void 无参数签名的方法的名称。
<bean id = "exampleBean" class = "examples.ExampleBean" init-method = "init"/>
- 下面是类的定义
public class ExampleBean {
public void init() {
// do some initialization work
}
}
3.3.2销毁回调
(1)DisposableBean的destroy方法
org.springframework.beans.factory.DisposableBean 接口指定一个方法
void destroy() throws Exception;
因此,您可以简单地实现上述接口,并且可以在destroy() 方法中完成如下工作
public class ExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work
}
}
(2)destroy-method属性指定销毁方法
- xml配置文件
<bean id = "exampleBean" class = "examples.ExampleBean" destroy-method = "destroy"/>
- 类的定义
public class ExampleBean {
public void destroy() {
// do some destruction work
}
}
3.4Bean依赖注入
3.4.1定义
- 依赖:bean对象的依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。
3.4.2依赖注入的作用
-
作用:降低程序间的耦合
依赖关系的管理,以后都交给spring来维护
在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明依赖关系的维护,就称之为依赖注入。
3.4.3依赖注入的方式
-
可以注入的数据(三类):
- 基本类型和String
- 其他bean类型(在配置文件中或者注解配置过的bean)
- 复杂类型/集合类型:(不演示)
-
注入的方式(三种):十分重要
- 构造函数注入
- set方法注入
- 基于xml的自动装配(bean标签的autowire属性(byName—>根据bean标签的id属性查找、byType——>根据bean标签的Class属性查找))
- 注解注入()
(1)构造函数注入
-
定义:用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。
-
说明:
- 使用的标签:constructor-arg
- 标签出现的位置:bean标签的内部
- 标签中的属性:
- type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型。
- index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始。
- name:用于指定给构造函数中指定名称的参数赋值。
- ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
- value:要注入的数据
- 优点:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
- 缺点:改变了bean对象的实例化方式,使得我们在创建对象时,如果用不到这些数据,也必须提供。
-
举例
<!--默认构造器方式--> <bean id="user" class="com.kuang.pojo.User"> <property name="name" value="张三"/> </bean> <!--通过有参构造创建对象。方式一:下标赋值--> <bean id="user" class="com.kuang.pojo.User"> <constructor-arg index="0" value="jerry"/> </bean> <!--通过有参构造创建对象。方式二:类型创建,不建议使用--> <bean id="user" class="com.kuang.pojo.User"> <constructor-arg type="java.lang.String" value="jarry"/> </bean> <!--通过有参构造创建对象。方式三:通过参数名,推荐使用--> <bean id="user" class="com.kuang.pojo.User"> <constructor-arg name="name" value="jarry"/> <constructor-arg name="birthday" ref="now"/> </bean> <!-- 配置一个日期对象 --> <bean id="now" class="java.util.Date"></bean>
(2)Set方式注入(常用)
- 定义:在类中提供需要注入成员的 set 方法
- 说明:
- 涉及的标签:property
- 出现的位置:bean标签的内部
- 标签的属性
- name:用于指定注入时注入的对象的名称(IOC容器会调用该对象的set方法)
- value:用于提供基本类型和String类型的数据
- ref:用于指定其他的bean类型数据。
- 优势:创建对象时没有明确的限制,可以直接使用默认构造函数。
- 弊端:如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
(3)基于xml的自动装配
- 定义:
- 自动装配是spring满足bean依赖注解一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性
- 三种自动装配方式:
- 在xmI中显示的配置(ByName、ByType)
- 注解显示配置
- 隐式的自动装配bean 【重要】
①搭建环境
public class Cat {
public void shout(){
System.out.println("喵喵");
}
}
public class Dog {
public void shout(){
System.out.println("旺旺");
}
}
public class Person {
private Cat cat;
private Dog dog;
private String name;
@Override
public String toString() {
return "Person{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
//getting、setting
}
②ByName自动装配
- byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanId
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="person" class="com.kuang.pojo.Person" autowire="byName">
<property name="name" value="小海子"/>
</bean>
③ByType自动装配
- byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean
<bean class="com.kuang.pojo.Cat"/>
<bean class="com.kuang.pojo.Dog"/>
<bean id="person" class="com.kuang.pojo.Person" autowire="byType">
<property name="name" value="小海子"/>
</bean>
④小结
- byname的时候,需要保证所有bean的id唯一 ,并且这个bean需要和自动注入的属性的set方法的值一致。
- bytype的时候, 需要保证所有bean的class唯一 ,并且这个bean需要和自动注入的属性的类型一致;全局唯一,id属性可以省略。
(4)基于注解的自动装配
-
jdk1.5支持的注解,Spring2.5支持注解
-
作用:和在xml配置文件中的bean标签中写一个标签的作用是一样
-
注解:
注解 功能 使用前提 @Autowired 根据属性类型进行自动装配
即通过byType——>bean标签的class属性方式实现/ @Qualifer 根据属性名称(由@Component的value值指定)进行注入
即通过byName——>bean标签的id属性实现在该属性注入的基础上 @Resource 根据类型注入,可以根据名称注入(默认根据类型注入) / @Value 注入普通类型属性 /
3.4.4详讲@Autowired、@Qualifer、@Resource、@Value
- spring的IOC底层实际上就是一个Map结构容器,所谓**key 就是 bean标签 中的 id,value 则是对应 bean标签 中的 class**
(1)@Autowired
①用在何处?
- Spring 2.5 引入了 @Autowired 注释,可以对类成员变量、方法及构造函数、参数等进行标注【主要还是用在变量和方法上】,完成自动装配的工作。
- 使用@Autowired注解注入的属性,该属性不需要这个类提供set方法,方便快捷
- @Autowired作用就和在xml配置文件中的bean标签中写一个
< property >
标签的作用是一样的。 - @Autowired自动装配首先会在IOC容器中跳过key直接去容器中找到对应的属性(bean标签中的Class)!也就是说与key无关
②三种情况
- 容器中有==唯一的一个bean对象类型(Class类路径)==和被@Autowired修饰的变量类型匹配,就可以注入成功!
- 容器中没有一个bean对象类型和被@Autowired修饰的变量类型匹配,则注入失败运行报错。
- 容器中有==多个bean对象类型和被@Autowired修饰的变量类型匹配,则根据被@Autowired修饰的变量名寻找==,找到则注入成功【重点】
(2)@Qualifer
①用在何处
- @Qualifier的作用是在按照类中注入的基础之上再按照名称注入。
- 它在给类成员注入时不能单独使用(但是在给方法参数注入时可以单独使用)
- @Qualifier常常组合@Autowired一起使用,用来==指明具体名字的自动装配==
②使用情况
- UserDaoImpl2类
- UserService3类注入UserDaoImpl2类
(3)@Resource
-
@Resource由J2EE提供,默认是按照byName自动注入(通过名字自动注入),
-
@Resource有两个重要的属性,name和type
当然默认是通过name属性
type属性多此一举,还不如用@Autowired
-
@Resource 相当于 @Autowired + @Qualifier
举例:
@Autowired @Qualifier(value="userDaoImpl2") //相当于 @Resource(name="userDaoImpl2")
(4)@Value
- @Value专门用来注入基本类型和String类型数据
- @Value注解有一个value 属性:用于指定数据的值。它可以使用spring中SpEL(也就是spring的EL表达式)。SpEL的写法:${表达式},当然也可以类似mybatis中的 #{表达式} 的写法
@Value("#{2*3}") //#写法 表示6
private int age;
@Value("178") //普遍写法 178
private int height;
@Value("${man.weight}") //SpEL的写法一般操作配置文件中数据
private int weight;
(5)辨别@Autowired和@Resource的异同
- 相同:两者都是用来自动装配的,都可以放在属性字段上
- 不同:
-
实现方式不同:
- @ Autowired 通过byType的方式实现
- @Resource 默认通过byname的方式实现
-
找的过程不同:
- 若@ Autowired 通过byType的方式找不到,则报错
- 若@Resource 默认通过byname的方式找不到,则通过byType实现。如果两个都找不到的情况下,就报错。
-
实现方式不同: