Spring技术总结02-IoC

一、概述

Ioc:Inverse of Control的简写,为 控制反转,指把创建对象交给Spring进行管理。即不通过new 对象的方式在dao、service等Impl类中获取对象,而是通过读取xml文件或者配置类的方式创建一个统一管理所有对象的ApplicationContext类,通过**getBean()**的方法获取所需对象。

Spring中的IoC容器就是IoC思想的一个落地产品实现。IoC容器中管理的组件也叫做bean。

二、基于xml方式管理bean

1.环境搭建

1.根据上一篇文章,已经按要求重新创建了一个项目。

image-20241031163048915

2.创建实体类和xml配置文件

配置文件创建方式如下图

image-20241031164148051

实体类创建

image-20241031165746906

image-20241031165803766

image-20241031165824485

2.管理bean对象——基本操作

在spring01.xml上编写语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!--1.通过无参构造函数创建实例化的组件,配置方法
<bean id="" class="">
id: 组件的标识,自定义名字,方便读取对象
class: 组件的全限定符,即全类名
` 默认是单例模式,声明两个相同的组件信息,会实例化两个组件对象
-->
<bean id="user1" class="com.ldy.bean.User"/>

<!--2.基本属性注入
在bean标签中添加<property name="" value/ref="" >标签
name:组件set方法去掉set后面一个字母小写的规则命名,例如setName -> name
value: 直接赋值即可,注意传值类型
ref: 其他组件的id
注意:若set方法中的参数是基本类型,用value直接赋值,若是其他类,要用ref取相应的id。
在组件中一定要给属性注入的属性添加set方法!!!
-->
<bean id="user2" class="com.ldy.bean.User">
<property name="name" value="白小纯"/>
<property name="age" value="18"/>
<property name="id" value="1"/>
<property name="sex" value="男"/>
</bean>

<!--3.对象类型属性注入-->
<bean id="userDao" class="com.ldy.dao.impl.UserDaoImpl">
<property name="user" ref="user2"/>
</bean>

<!--4.构造器属性注入
<constructor-arg 进行DI配置
value : 直接赋值
ref : 所要引用对象的bean的id
-->
<bean id="userService" class="com.ldy.service.impl.UserServiceImpl">
<constructor-arg ref="userDao"/>
</bean>

<!--多参数构造器的情况
三种方式
1.按构造器的参数默认顺序配置
2.按构造器的参数的参数名定位赋值
3. 按构造器的参数的索引值定位赋值,从0开始
以下的实现在上述实体类中并没有设置,从之前的文件复制修改的,了解即可
-->
<!--1.按构造器的参数默认顺序配置 -->
<bean id="userService2" class="com.ldy.service.impl.UserServiceImpl">
<constructor-arg ref="userDao"/>
<constructor-arg value="ggbone"/>
<constructor-arg value="18"/>
</bean>
<!--2.按构造器的参数的参数名定位赋值-->
<bean id="userService3" class="com.ldy.service.impl.UserServiceImpl">
<constructor-arg name="age" value="18"/>
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="name" value="ggbone"/>
</bean>
<!--3. 按构造器的参数的索引值定位赋值,从0开始-->
<bean id="userService4" class="com.ldy.service.impl.UserServiceImpl">
<constructor-arg index="2" value="18"/>
<constructor-arg index="0" ref="userDao"/>
<constructor-arg index="1" value="ggbone"/>
</bean>

这里我们写一个测试类进行测试,看看是否能成功获取对象并执行方法。

测试时的ioc容器对象创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//    创建ioc容器
// 有四个实现类,最常用的是ClassPathXmlApplicationContext
/*
推荐创建方式, new 实现类(String...),传入一个或多个ioc配置文件名称
配置文件的名称是根据编译后的target文件中的的classes进行查找的,由于resources下面直接创建了xml文件
,所以相对应的classes下直接有相应的xml文件
*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-03.xml");
//获取Bean对象
//方法1,只传入bean的id名,需要强转,不推荐
Person person = (Person)applicationContext.getBean("person");
//方法2,传入bean的id和相应的Class类
Person person1 = applicationContext.getBean("person", Person.class);
//方法3, 只传入要获取的类的Class
//要求:
// 使用该方法必须在配置文件里,有且只能有该类的一个Bean,不能有多个
// 若多个,则报异常 NoUniqueBeanDefinitionException
Person person2 = applicationContext.getBean(Person.class);
//此外,可以通过该类的父类的Class获取对象
Animal animal = applicationContext.getBean(Animal.class);

推荐在test文件夹下面创建测试类,如下图所示。

image-20241031184949948

这里我们可以看到成功的获取了对象和实现了相应的方法

image-20241031185027013

3.其他细节内容

以下代码均是之前学习时的产物,没必要重新写了,就复制过来了

1.bean标签的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--    类似tomcat的servlet的生命周期
init-method: 在该对象创建后就调用指定的对象的一个函数,必须是无参、public且返回值为void
destory-mothod: 该对象销毁时调用的方法,要求同上
注意:简而言之就是一旦管理这个bean的类被创建后就会调用init-method,类close()后就会调用destory-method。与通过getBean()获取对象无关,因为bean对象早就创建了,这只是引用创建的对象而已。
-->
<!-- scope属性指定该Bean每次被Ioc容器对象使用getBean()方法获取后是否是单例的
prototype: 多例
singleton: 单例, 默认值
注意:
容器管理范围:
单例模式下的 Bean 由 Spring 容器管理其整个生命周期,包括创建和销毁。
多例模式下的 Bean 由客户端代码管理其生命周期,Spring 容器仅负责创建。
所以多例模式下的Bean在Ioc容器close()后不会被调用destroy-method
-->
<bean id="javaBean2" class="com.ldy.ioc04.JavaBean" init-method="init" destroy-method="destroy" scope="singleton" />

2.获取原始类

1
2
3
4
5
6
7
<!--.直接通过原始类赋值获取DataSource-->
<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>

3.配置文件赋值

1
2
3
4
5
6
7
8
9
10
11
<!--通过配置文件赋值给原始类获取DataSource
<context:property-placeholder 引用配置文件 location="classpath:druid.properties"指明配置文件在类路径下
使用${变量名}来获取配置文件中的属性值
-->
<context:property-placeholder location="classpath:druid.properties"/>
<bean id="dataSource3" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${xxx.username}"/>
<property name="password" value="${xxx.password}"/>
<property name="driverClassName" value="${xxx.driverClassName}"/>
<property name="url" value="${xxx.url}"/>
</bean>

4.配合注解进行包扫描

1
2
3
4
<!--指定包下面进行注解扫描
base-package: 包名,该包以下的所有类和子包中的所有类都会被扫描
-->
<context:component-scan base-package="com.ldy"/>

三、基于配置类的方式管理bean

Spring从2.5版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化Spring的xml配置。

Spring通过注解实现自动装配:

  1. 引入依赖
  2. 开启组件扫描
  3. 使用注解定义Bean
  4. 依赖注入

1.注解介绍

基本的类上注解

image-20241031192752509

@ComponentScan用于批量注册bean。

这个注解会让spring去扫描某些包及其子包中所有的类,然后将满足一定条件的类作为bean注册到spring容器容器中。

@Autowired注入
单独使用@Autowired注解,默认根据类型装配(byType)

@Autowired注解有一个required属性,默认值是true,表示在注入的时候要求被注入的Bean必须存在,如果不存在则报错。如果required属性设置为false,表示注入的Bean存在或者不存在都没关系,存在就注入,不存在也不报错。

@Qualifier搭配@Autowired使用

若@Autowired注入的类有多个实现类,需要指定某个实体类时,在@Autowired注解下面添加@Qualifier(“指定实现类的id”)来指定注入的实现类。

@Resource整合了@Autowired和@Qualifier一起的功能

  1. 指定@Resource中的name,则根据名称装配
  2. 未指定name时,则根据属性名装配
  3. 未指定name,属性名也不一致,则根据类型装配

使用该注解需要导入如下依赖

1
2
3
4
5
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>

@PropertySource配置文件引用

示例:@PropertySource(value=”classpath:jdbc.properties”),与xml的配置文件路径写法一致。

2.实际操作

1.简单操作

创建配置类

image-20241031195230545

给各个组件添加注解,以下是其中一个类

image-20241031195322823

给之前写的类添加上上述注解之后,用AnnotationConfigApplicationContext获取ApplicationContext类进行测试。

image-20241031194955240

2.详细内容

之前所学习时的配置类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
使用配置类而不使用xml进行ioc容器的配置
@Configuration ioc配置类
@@ComponentScan 包扫描
@PropertySource 配置文件引用
* */
@Configuration
@ComponentScan(basePackages = "com.ldy")
@PropertySource(value="classpath:jdbc.properties")

public class JavaConfigure {
// bean由@Bean注解标注,并且对象类型为方法返回的类型,id默认为方法名
// 配置文件的Di配置由参数(注解+参数声明)来获取配置参数,以便进行依赖注入
@Bean
public DataSource dataSource(@Value("${xxx.url}") String url, @Value("${xxx.driverClassName}") String driverClassName,@Value("${xxx.username}") String username, @Value("${xxx.password}") String password){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
// 配置类的Di中,参数作为方法参数被传入
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
}

四、总结

全注解开发就是不再使用spring配置文件了,写一个配置类来代替配置文件。用注解配置还是很方便的。