荐 Spring 学习笔记①:IoC容器、Bean与注入
程序员文章站
2022-04-19 13:23:52
IoC容器、Bean与注入...
文章目录
收藏这三篇笔记,完整回顾Spring常见问题及使用方式速查:
0. 基本概念
- 控制反转(IoC):被调用者的实例不再由调用者创建,而是由 Spring 容器创建,这称为控制反转。
- 依赖注入(DI):Spring 容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,这样,调用者通过 Spring 容器获得被调用者实例,这称为依赖注入。
文档、JAR包:官方仓库地址。
1. 示例pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
2. IOC容器
二者相比,前者功能简单且没有初始化自检。
2.1 BeanFactory(不常用)
Resource resource = new ClassPathResource("ApplicationContext.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
beanFactory.getBean("$BeanName");
2.2 ApplicationContext(示例)
ApplicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="service0" class="Service">
<constructor-arg name="name" type="java.lang.String" value="service0:name"/>
</bean>
<!-- 构造标签详见下 -->
<bean id="dao0" class="DAO">
<constructor-arg name="id" type="java.lang.Integer" value="7"/>
<constructor-arg name="name" type="java.lang.String" value="dao0"/>
<constructor-arg name="service" type="Service" ref="service0"/> <!-- 实例依赖-->
</bean>
</beans>
Service.java
:
@Data // 使用了 lombok
@AllArgsConstructor
public class Service {
private String name;
}
DAO.java
:
@Data
@AllArgsConstructor
public class DAO {
private Integer id;
private String name;
private Service service; // 依赖Service,采用`ref`
}
测试代码:
public class Main {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
DAO a = (DAO)context.getBean("dao0");
System.out.println(a);
}
}
目录结构为:
2.3 通过注解进行装配
通过注解进行装配,编写方便、无需写xml标签文件,但若有更改需要重编译。
2.3.1 常用注解
-
@Component
:可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可;将会将其实例化和注入依赖并加入IoC容器。 -
@Repository
:用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与@Component
相同。 -
@Service
:通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与@Component
相同。 -
@Controller
:通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与@Component
相同。 -
@Autowired
:用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。 -
@Resource
:其作用与 Autowired 一样。其区别在于@Autowired
默认按照 Bean 类型装配,而@Resource
默认按照 Bean 实例名称进行装配。 -
@Qualifier
:与@Autowired
注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由@Qualifier
注解的参数指定。
附 · 使用JacaConfig类配置Bean所用到的注解:
-
@Configuation
等价于<beans></beans>
,不可用于标注final
、匿名类,必须为静态类。 -
@Bean
等价于<bean></bean>
。 -
@ComponentScan
等价于<context:component-scan base-package="com.dxz.demo"/>
。
2.3.2 示例:使用注解装配的MVC
目录结构:
此处可以选择构建一个配置类以取代 ApplicationContext.xml
:
@Configuration // 该注解同样也需要被扫描到,因此同样要在xml文件中进行配置
// @ComponentScan("MVC") // 等价于<context:component-scan base-package="MVC"/> //由于目录结构中该配置类与MVC同包,会被xml配置直接扫描到,因此此处不需要。
public class SpringConfiguration {
// 等价于
// <bean id="user0" class="xxx.User">
// <constructor-arg name="id" type="java.lang.Integer" value="0"></constructor-arg>
// <constructor-arg name="name" type="java.lang.String" value="Jack"></constructor-arg>
// </bean>
@Bean(name = "user0")
public User UserBeanJack(){
return new User(0, "Jack");
}
}
构建一个控制器:
@Controller
public class UserController {
@Resource // 此处使用Resource,默认采用首字母小写的形式去找到类, 等价于 getBean("userService")
private UserService userService;
public void acceptRequest(ApplicationContext context, Object request){
System.out.println("Accept request.");
User user = (User)context.getBean("user0"); // 假设根据Request构建Model
System.out.println("Controller find user: " + user);
this.userService.service(user); // 找到Model对应的业务
System.out.println("MVC Controller Over.");
}
}
构建Model层的各个组件:,首先是业务逻辑:
@Service("userService") // 默认是类名首字母小写, 等价于 <bean id="userService" class="xxx.UserService"/>
public class UserService {
private UserDao userDao;
@Autowired // 此处使用自动装配的形式
public UserService(UserDao userDao){
this.userDao = userDao;
}
public void service(User user){
System.out.println("MVC Service sth. with " + user);
this.userDao.save(user);
System.out.println("MVC Service Over.");
}
}
数据持久层:
@Repository("userDao") // 等价于 <bean id="userDao" class="xxx.UserDao"/>
public class UserDao {
public void save(User user){
System.out.println("数据库已保存" + user);
}
}
一个POJO类:
@Data // 使用了lombok简化代码编写
@AllArgsConstructor
public class User {
private Integer id;
private String name;
}
最后,还需要让Spring扫描到这些注解:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.xsd">
<!-- 增加第3, 8, 9行-->
<context:component-scan base-package="MVC"/>
</beans>
测试:
public class Main {
public static void main(String[] args){
final ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserController userController = (UserController)context.getBean("userController");
// 注意不能用 UserController userController = new UserController();
userController.acceptRequest(context, null);
}
}
/*打印结果如下:
Accept request.
Controller find user: User(id=0, name=Jack)
MVC Service sth. with User(id=0, name=Jack)
数据库已保存User(id=0, name=Jack)
MVC Service Over.
MVC Controller Over.*/
- 需要注意的是,所有的实例都要从IoC容器里获取,自行new出来的实例不会被Spring注入,会导致Null异常。
2.3.3 示例所使用的流程图
2.4 往容器内配置Bean的三种方式
- 使用xml进行配置,bean的名字为
id
字段。 - 使用JavaConfig进行配置(
@Configuration
),默认bean的名字为@Bean
所标注的方法名。 - 使用注解进行配置,默认Bean的名字为类名(首字母小写)。
2.5 @Autowired 和 @Resource 的区别
- 前者默认按类型装配,当容器内存在多种同一类型的Bean时,需要使用
@Qualifier(value = "$BeanName")
才可以找到被注入的对象。 - 后者在不指定
name
字段时,也是以同一类型的Bean进行注入,若存在多个同类型对象,会报错;当指定name
字段时等同于getBean("$BeanName")
(某些框架中,找不到会再根据类型寻找)。 - 二者都可以标注在
setter()
或构造器方法上;@Autowired
不被推荐在字段上直接标注(缺点见下文)。
2.5.1 注入方式及字段注入的缺点
对于二者,注入方式都有三种:构造器注入、setter注入、field注入,区别如下:
public class UserController {
@Autowired // field注入,也称为反射注入或字段注入,对访问级别无要求
private UserService userService;
@Autowired // 构造器注入(需要为public) 适用于强制依赖项
public UserController(UserService userService){
this.userService = userService;
}
@Autowired // setter注入(需要为public) 适用于可选依赖项
public void setUserService(UserService userService){
this.userService = userService;
}
}
使用字段注入,其最大缺点为:
- 类会和Spring强耦合,由于无构造器或setter方法,无法在容器外使用。
- 无法使用属性注入的方式构建不可变对象。
- 同第一小点,在测试时无法脱离容器进行测试,即 new 出来的 Mock 对象无法被引入测试单元。
- 强依赖和可选依赖都被隐藏,无法区分,违背单一职责原则。
3. Bean的常见概念
3.1 Bean的常用属性
属性名称 | 描述 |
---|---|
id | 是一个 Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成 |
name | Spring 容器同样可以通过此属性对容器中的 Bean 进行配置和管理,name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,使用类的全限定名 |
scope | 用于设定 Bean 实例的作用域,其属性值有 singleton(单例)、prototype(原型)、request、session 和 global Session。其【默认值】是 singleton。 |
property | 元素的子元素,用于调用 Bean 实例中的 Set 方法完成属性赋值,从而完成依赖注入。该元素的 name 属性指定 Bean 实例中的相应属性名 |
constructor-arg | 元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型 |
constructor-arg:name/index | 标定一个bean的属性名称/构造函数的第index个参数,用于初始化实例 |
ref/value | 和 等元素的子元素,用于直接指定一个引用/常量值;亦可作为初始化时注入集合类元素的标签 |
list | 用于封装 List 或数组类型的依赖注入 |
set | 用于封装 Set 或数组类型的依赖注入 |
map | 用于封装 Map 或数组类型的依赖注入 |
entry | |
factory-bean | 当class是一个工厂类时,指定其作为工厂 |
factory-method | 当class是一个工厂类时,使用该标签标注的静态方法(或若已指定一个factory-bean标注的实例)得到实例 |
autowire | 是否自动装配,详见下文表格 |
3.1.1 自动装配
属性取值 | 描述 |
---|---|
byName | 根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。 |
byType | 根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。 |
constructor | 根据构造方法的参数的数据类型,进行 byType 模式的自动装配。 |
autodetect | 如果发现默认的构造方法,则用 constructor 模式,否则用 byType 模式。 |
no | 【默认】情况下,不使用自动装配,Bean 依赖必须通过 ref 元素定义。 |
3.2 Bean的作用域
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。Spring3 为 Bean 定义了五种作用域,具体如下。
-
singleton
:单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 【默认】的作用域。 -
prototype
:原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。 -
request
:在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。 -
session
:在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。 -
global Session
:在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。
3.3 Bean的生命周期
- 根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean,包含以下四个预处理项目:
-
ResouceLoader
加载配置信息; -
BeanDefintionReader
解析配置信息,生成若干个BeanDefintion
; -
BeanDefintion
由BeanDefintionRegistry
管理起来; -
BeanFactoryPostProcessor
对配置信息进行加工(也就是处理配置的信息,一般通过PropertyPlaceholderConfigurer
来实现);
- 利用依赖注入完成 Bean 中所有属性值的配置注入。
- 如果 Bean 实现了
BeanNameAware
接口,则 Spring 调用 Bean 的setBeanName()
方法传入当前 Bean 的id
值。 - 如果 Bean 实现了
BeanFactoryAware
接口,则 Spring 调用setBeanFactory()
方法传入当前工厂实例的引用。 - 如果 Bean 实现了
ApplicationContextAware
接口,则 Spring 调用setApplicationContext()
方法传入当前ApplicationContext
实例的引用。 - 如果
BeanPostProcessor
(BBP) 和 Bean 关联,则 Spring 将调用该接口的预初始化方法postProcessBeforeInitialzation()
对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。 - 如果 Bean 实现了
InitializingBean
接口,则 Spring 将调用afterPropertiesSet()
方法。 - 如果在配置文件中通过
init-method
属性指定了初始化方法,则调用该初始化方法。 - 如果
BeanPostProcessor
和 Bean 关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()
。此时,Bean 已经可以被应用系统使用了。 - 如果在
<bean>
中指定了该 Bean 的作用范围为scope="singleton"
,则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在<bean>
中指定了该 Bean 的作用范围为scope="prototype"
,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。 - 如果 Bean 实现了
DisposableBean
接口,则 Spring 会调用destory()
方法将 Spring 中的 Bean 销毁;如果在配置文件中通过destory-method
属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
本文地址:https://blog.csdn.net/Shenpibaipao/article/details/107141737
上一篇: 1 图像基本操作
下一篇: 企业的品牌营销推广怎么做?附详细步骤
推荐阅读
-
PHP进阶学习之依赖注入与Ioc容器详解
-
荐 Spring 学习笔记①:IoC容器、Bean与注入
-
Spring IOC DI依赖注入 IOC容器 Spring Bean的声明周期
-
Spring学习笔记——一、控制反转(IOC)和依赖注入(DI)
-
Spring 整体学习(IOC容器 DI依赖注入 AOP 面向切面编程(动态代理) 配置注入 注解注入 )
-
spring3学习笔记(六)bean属性的注入
-
Spring IOC容器学习,Bean的学习;
-
JavaWeb学习笔记-spring-04-ioc-bean装载
-
Spring IOC学习笔记(二)—— Bean装配方式
-
PHP进阶学习之依赖注入与Ioc容器详解