🔗 官方文档
在 Spring 中,使用 org.springframework.context.ApplicationContext
接口代表Spring IoC 容器,负责实例化、配置和组装 bean。容器通过读取配置元数据来获取关于要实例化、配置和组装哪些对象的指令。
官方的解释:将你的业务对象通过配置数据元的形式(xml或java bean、注解)存储到 Spring 容器中,方便后续的使用。
其中xml的形式,在 Spring 5的时候,已经很少使用了,基本都是 Java Bean 和 注解的形式来配置数据元。
Java Bean 的形式:
主要使用的注解是 @Configuration 、@Bean、@Component等
注解的形式:
主要使用的注解是 @Autowired、@Resource 等,它们在实现上,实际是通过 BeanPostProcessor 的接口实现来处理的。
⭐️ 特别提示:本文的核心内容,放在了 Spring IOC 容器上。
现在的 SpringBoot 项目中,使用最多的是:
@Primary
@Bean
public Cpu cpu() {
return new Cpu("先进的CPU");
}
简单的分为两种场景,第一种是和Spring
框架耦合性较大的获取方式;第二种,是耦合度小的方式。
Spring
提供了 BeanFactory
接口,可以使用 BeanFactoryAware
接口获取该对象,然后在通过 BeanFactory
接口提供的获取Bean 的方法即可。
比如:
package org.feng.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
/**
* BeanFactoryAware 实现类:可以获取到 BeanFactory 实例
*
* @version V1.0
* @author: fengjinsong
* @date: 2022年06月29日 14时03分
*/
@Component
@Slf4j
public class BeanFactoryAwareImpl implements BeanFactoryAware {
private static BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("使用 BeanFactoryAware 获取到 BeanFactory {}", beanFactory);
BeanFactoryAwareImpl.beanFactory = beanFactory;
}
public static <T> T getBean(Class<T> clazz) {
return beanFactory.getBean(clazz);
}
public static <T> T getBean(String name, Class<T> clazz) {
return beanFactory.getBean(name, clazz);
}
public static Object getBean(String name) {
return beanFactory.getBean(name);
}
}
既然BeanFactory
本身是可以这样操作,那么作为它的子接口,ApplicationContext
也能达到同样的相关,甚至ApplicationContext
的功能更多,因为它本身还继承了其他的接口。这个详细见本项目中的类:ApplicationContextAwareImpl
。
另外,在Spring Bean 的生命周期中,也可以通过对应的接口获取Bean对象。
一般这类操作,会用在初始化数据的场景上,业务场景用的较少。
推荐使用这种方式。
对应的注解中,使用最多的就是 @Resource
、@Autowired
。
个人建议,使用Resource注解,具体原因见本文中的 【6 常见注解】 中的 6.12 小节。
注意:上图只说明Spring中的单例Bean。不讨论多例和懒加载的情况。
文字描述:
首先,在启动Spring应用时,会通过扫描包及Class文件,对使用了特殊注解、实现了特定接口的类信息进行组装,获得对应的BeanDefinition。根据BeanDefinition,初始化对应的单例Bean对象。
初始化的过程是,根据Java的反射获取到对应的构造器,调用构造器方法。然后注入属性、以及其他的一些初始化操作。
public interface ApplicationContextAware extends Aware {
/**
* 可以看到是 set 了 ApplicationContext 的实例
*/
void setApplicationContext(ApplicationContext var1) throws BeansException;
}
该接口用来获取 ApplicationContext 的实例,并使用它进行一些操作。
继承关系图如上,本次主要涉及到 BeanFactory 的一些说明。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
用于在对象Bean的生命周期中,自定义一些配置。
提供给使用者,用来在自定义配置之前或之后,执行一些操作。
这个接口的实现类,可以有多个,但是只对单例,非懒加载的Bean对象有效。使用时需要额外注意。
暂不说明,有兴趣的朋友看看下面的文章:
https://blog.csdn.net/liyantianmin/article/details/81017960
可以通过实现这个接口,来获取 beanName。
可以通过实现这个接口,来获取 BeanFactory。
本小节的内容,来自官网的翻译:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-basics
Spring IoC 容器管理一个或多个 bean。这些 bean 是使用您提供给容器的配置元数据创建的(例如,以 XML
<bean/>
定义的形式)。在容器本身中,这些 bean 定义表示为BeanDefinition
对象,其中包含(以及其他信息)以下元数据:
属性 含义 Class Instantiating Beans 类信息,一般是包名+类名 Name Naming Beans 别名 Scope Bean Scopes 创建对象的范围 Constructor arguments Dependency Injection 构造器的参数列表 Properties Dependency Injection 依赖注入的属性 Autowiring mode Autowiring Collaborators 自动注入模式 Lazy initialization mode Lazy-initialized Beans 懒加载 Initialization method Initialization Callbacks 初始化方法回调 Destruction method Destruction Callbacks 销毁方法回调
表示将一个类创建对象,注入到Spring中。不区分指定场景。
控制器注解,在经典的分层中,最接近前端的位置。标注类和前端请求、响应相关联。
有时也会和 Scope 搭配着使用,指定为原型模式。
业务处理层注解,在经典的分层中,用于业务实现层的实现类。将对应的对象注入容器中。
仓储层的注解是,标志将指定类的对象注入到容器中。
在经典分层的 DAO,数据访问层使用。一般和数据源打交道。
配置注解,在创建配置类的时候,需要使用这个注解。
该注解中,一般会配置多个Bean。
方法级的注解,用在带有返回值的方法上。功能上和老版的Spring 的 bean 标签是一样的。
标注创建对象的范围。
一般常用的是,singleton 和 prototype。
Scope | 描述 |
---|---|
singleton | (Spring 默认)将单个 bean 定义限定为每个 Spring IoC 容器的单个对象实例。在spring容器启动时,会将其初始化,并存到容器中。每次获得到的都是同一个对象。 |
prototype | 将单个 bean 定义限定为任意数量的对象实例。只有在 getBean 的时候,才会创建对象、初始化。 |
request | 将单个 bean 定义限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,该实例是在单个 bean 定义的后面创建的。仅在 Web 感知 Spring 的上下文中有效ApplicationContext 。Url不发生变化,就是同一个对象。 |
session | 将单个 bean 定义限定为 HTTP 的生命周期Session 。仅在 Web 感知 Spring 的上下文中有效ApplicationContext 。当前会话未发生改变(一般是浏览器未关闭) |
application | 将单个 bean 定义限定为ServletContext . 仅在 Web 感知 Spring 的上下文中有效ApplicationContext 。 |
websocket | 将单个 bean 定义限定为WebSocket . 仅在 Web 感知 Spring 的上下文中有效ApplicationContext 。 |
同样的功能的还有 Ordered 接口。
它们会控制Bean的执行顺序,也就是在注入时,具体注入的是哪个对象。
指定的数值越小,表示优先级越高。
标注对象的初始化为懒加载。延迟加载单例Bean,减少容器启动时的时间。在第一次使用该Bean时,会创建并初始化。
根据属性类型进行自动装配。一般建议使用Resource
注解。
关于它,官方的几个例子是不错的:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-autowired-annotation
指定需要查找并注入的bean 的 beanName。与@Autowired
一起使用
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-resource-annotation
关于 Resource 的官方说明,较少,以下是本人的一些认识:
可以根据类型或指定名称进行属性对象注入。
不指定 name 时,按照类型注入。比如:以下的使用,需要有一个数据类型为 Cpu 的bean。容器中找到多个同类型的bean时,会报错。
@Resource
private Cpu cpu;
当Spring容器中存在多个同类型bean,会报错:意思是,期待找到一个bean,但是找到了多个。因此不知道该注入哪一个,此时需要指定名称。
No qualifying bean of type 'org.feng.entity.Cpu' available: expected single matching bean but found 3: cpu,oldCpu,cpuWithBeanName
当指定name是,会按照 name 去容器中找对应的bean。如果通过名字找不到对应的bean,会直接报找不到对应名称的bean。
该注解是javax中的一个注解。而Autowired是 Spring框架中的注解。
从配置文件中获取属性值。
在 application.properties 中配置:
# 应用名称
spring.application.name=spring-learn
# 应用服务 WEB 访问端口
server.port=8080
computer.server1=sssssssssss
使用时,在对应的类中:
// 当没有配置对应属性,使用冒号,后面写上属性的默认值
@Value("${computer.server:北京某地}")
private String server;
// 从配置中获取对应的属性值
@Value("${computer.server1}")
private String server1;
初始化,属于生命周期的一部分,在Bean创建后,自定义初始化的逻辑。
销毁前,属于生命周期的一部分。在Bean销毁时执行指定逻辑,一般用于释放资源。
https://start.aliyun.com/
这一步填写好信息之后,就直接点击【Next】。
本次练习,只涉及到 Spring 本身,为了方便调试,使用 Spring Web 的依赖。
选择之后,点击 【Finish】即可。
Attention:注意整个过程中,网络连接一定保持正常!还有就是事先配置好Maven环境。
首先删除选择中的目录及文件。
然后在 pom文件中调整 Java 的版本,调整为 11版本。
本次不使用测试包,因此同时删除测试目录以及依赖。随后增加 Lombok 依赖备用。
目录结构就变成了以下的模样:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.feng</groupId>
<artifactId>spring-learn</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-learn</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.4.1</spring-boot.version>
<lombok.version>1.18.22</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<mainClass>org.feng.SpringLearnApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
运行 SpringLearnApplication 中的 main 方法即可。
Gitee 仓库地址如下:
https://gitee.com/fengsoshuai/spring-learn
实现了Spring中的一些注解,接口。从扫描包,到BeanFactory,Bean的初始化等。
博客地址:
https://blog.csdn.net/FBB360JAVA/article/details/124437455
Gitee 仓库:
https://gitee.com/fengsoshuai/demo-spring