该文篇幅较长,主要包括两个章节,第一个章节主要是对Spring的注入方式进行讲解与演示。第二个章节主要通过源码方式对注入流程进行讲解。
Spring IOC 依赖注入
依赖注入的方式
在Spring中,依赖注入方式通常分为两种形式,手动注入和自动注入形式。
手动注入: 手动注入通常来说会比较麻烦,但是逻辑也会比较清晰,不容易出现问题。
自动注入: 使用起来方便,但是通常情况下依赖会变得模糊不清,不方便维护管理,并且如果被注入的依赖名称发生改变的时候还会导致注入失败。
常规依赖注入
set方式注入
set注入,顾名思义,他要求被注入的对象必须包含set方法,然后Spring通过调用对应属性的set方法进行注入赋值。
被注入对象信息:
public class SetterInjectionEntity {
private String id;
private String desc;
// 必须存在set方法从而进行注入
public void setId(String id) {
this.id = id;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "SetterInjectionEntity{" +
"id='" + id + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
手动模式
- • 基于XML文件配置方式:
XML配置:
<?xml versinotallow="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 https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="cn.phshi.domain.SetterInjectionEntity">
<property name="id" value="001"/>
<property name="desc" value="注入描述信息"/>
</bean>
</beans>
Java代码:
public class XmlDependencySetterInjectionDemo {
public static void main(String[] args) {
// 使用DefaultListableBeanFactory创建上下文
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 使用XmlBeanDefinitionReader读取xml文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 加载 XML 资源,解析并且生成 BeanDefinition
beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-setter-injection.xml");
// 依赖查找并且创建 Bean
SetterInjectionEntity entity = beanFactory.getBean(SetterInjectionEntity.class);
System.out.println(entity);
}
}
- • Java元注解方式:
public class AnnotationDependencySetterInjectionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(AnnotationDependencySetterInjectionDemo.class);
annotationConfigApplicationContext.refresh();
SetterInjectionEntity bean = annotationConfigApplicationContext.getBean(SetterInjectionEntity.class);
System.out.println(bean);
annotationConfigApplicationContext.close();
}
@Bean
public SetterInjectionEntity setterInjectionEntity() {
SetterInjectionEntity entity = new SetterInjectionEntity();
entity.setId("002");
entity.setDesc("注解进行set注入方式");
return entity;
}
}
- • API元信息注入:
最原生方式,直接通过构建BeanDefinition
信息进行注入。
public class ApiDependencySetterInjectionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
// 构建BeanDefinition信息
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(SetterInjectionEntity.class);
definitionBuilder.addPropertyValue("id", "003")
.addPropertyValue("desc","api元信息注入方式");
// 将BeanDefinition注册到BeanFactory中
defaultListableBeanFactory.registerBeanDefinition("setterInjectionEntity",definitionBuilder.getBeanDefinition());
SetterInjectionEntity bean = defaultListableBeanFactory.getBean(SetterInjectionEntity.class);
System.out.println(bean);
}
}
自动模式
创建一个新的注入的java对象:
public class SetterInjectionEntityHolder {
private SetterInjectionEntity setterInjectionEntity;
// 添加set方法方便注入
public void setSetterInjectionEntity(SetterInjectionEntity setterInjectionEntity) {
this.setterInjectionEntity = setterInjectionEntity;
}
@Override
public String toString() {
return "SetterInjectionEntityHolder{" +
"setterInjectinotallow=" + setterInjectionEntity +
'}';
}
}
XML配置:
<?xml versinotallow="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 https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 引入上面xml配置文件的内容 -->
<import resource="dependency-setter-injection.xml"/>
<bean class="cn.phshi.domain.SetterInjectionEntityHolder" autowire="byType"/>
</beans>
Java代码:
public class XmlDependencyAutowiringSetterInjectionDemo {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-autowiring-setter-injection.xml");
SetterInjectionEntityHolder bean = beanFactory.getBean(SetterInjectionEntityHolder.class);
System.out.println(bean);
}
}
构造器注入
被注入的对象:
public class ConstructorInjectionEntity {
private Long id;
private String decs;
public ConstructorInjectionEntity(Long id, String decs) {
this.id = id;
this.decs = decs;
}
@Override
public String toString() {
return "ConstructorInjectionEntity{" +
"id=" + id +
", decs='" + decs + '\'' +
'}';
}
}
// 支持对象
public class ConstructorInjectionEntityHolder {
private ConstructorInjectionEntity constructorInjectionEntity;
public ConstructorInjectionEntityHolder(ConstructorInjectionEntity constructorInjectionEntity) {
this.constructorInjectionEntity = constructorInjectionEntity;
}
@Override
public String toString() {
return "ConstructorInjectionEntityHolder{" +
"constructorInjectinotallow=" + constructorInjectionEntity +
'}';
}
}
手动注入
- • 基于XML文件配置方式:
XML配置:
<?xml versinotallow="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 https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="constructorInjectionEntity" class="cn.phshi.domain.ConstructorInjectionEntity">
<constructor-arg value="1"/>
<constructor-arg value="xml构造器注入方式"/>
</bean>
</beans>
Java代码:
public class XmlDependencyConstructorInjectionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-constructor-injection.xml");
ConstructorInjectionEntity bean = defaultListableBeanFactory.getBean(ConstructorInjectionEntity.class);
System.out.println(bean);
}
}
- • Java元注解方式:
public class AnnotationDependencyConstructorInjectionDemo {
private final ConstructorInjectionEntity constructorInjectionEntity;
public AnnotationDependencyConstructorInjectionDemo(ConstructorInjectionEntity constructorInjectionEntity) {
this.constructorInjectionEntity = constructorInjectionEntity;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(AnnotationDependencyConstructorInjectionDemo.class);
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-constructor-injection.xml");
applicationContext.refresh();
AnnotationDependencyConstructorInjectionDemo bean = applicationContext.getBean(AnnotationDependencyConstructorInjectionDemo.class);
System.out.println(bean.constructorInjectionEntity);
applicationContext.close();
}
}
- • API元信息注入:
public class ApiDependencyConstructorInjectionDemo {
public static void main(String[] args) {
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
// 读取配置中的对象实例注册到容器中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-constructor-injection.xml");
// 注册BeanDefinition
AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(ConstructorInjectionEntityHolder.class)
.addConstructorArgReference("constructorInjectionEntity")
.getBeanDefinition();
defaultListableBeanFactory.registerBeanDefinition("constructorInjectionEntityHolder",definition);
ConstructorInjectionEntityHolder bean = defaultListableBeanFactory.getBean(ConstructorInjectionEntityHolder.class);
System.out.println(bean);
}
}
自动注入
XML配置:
<?xml versinotallow="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 https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="dependency-constructor-injection.xml"/>
<bean name="constructorInjectionEntityHolder" class="cn.phshi.domain.ConstructorInjectionEntityHolder"
autowire="constructor"/>
</beans>
Java代码:
public class XmlDependencyAutowiringSetterInjectionDemo {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-autowiring-setter-injection.xml");
SetterInjectionEntityHolder bean = beanFactory.getBean(SetterInjectionEntityHolder.class);
System.out.println(bean);
}
}
字段注入
字段注入主要是使用
@Autowired
或者@Resource
标注在要对象注入的属性上面,当然,前提是被标注的对象以及标注的对象两个都要被Spring进行管理。
被注入的对象:
public class CommonInjectionEntity {
private Long id;
private String desc;
public CommonInjectionEntity(Long id, String desc) {
this.id = id;
this.desc = desc;
}
@Override
public String toString() {
return "CommonInjectionEntity{" +
"id=" + id +
", desc='" + desc + '\'' +
'}';
}
}
- • Java元注解方式:
public class AnnotationDependencyFieldInjectionDemo {
@Autowired
private CommonInjectionEntity autowiredCommonInjectionEntity;
@Resource
private CommonInjectionEntity resourceCommonInjectionEntity;
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(AnnotationDependencyFieldInjectionDemo.class);
annotationConfigApplicationContext.refresh();
AnnotationDependencyFieldInjectionDemo bean = annotationConfigApplicationContext.getBean(AnnotationDependencyFieldInjectionDemo.class);
System.out.println("Autowired注入的值:"+bean.autowiredCommonInjectionEntity);
System.out.println("Resource注入的值:"+bean.resourceCommonInjectionEntity);
// 返回true
System.out.println("Autowired注入和Resource注入的值" + (bean.autowiredCommonInjectionEntity == bean.resourceCommonInjectionEntity));
annotationConfigApplicationContext.close();
}
@Bean
public CommonInjectionEntity commonInjectionEntity() {
return new CommonInjectionEntity(1L, "字段注入方式测试");
}
}
方法注入
和字段注入相仿,只不过字段注入是使用注解标注在字段上面,而方法注入是标注在方法上面。
Java代码:
public class AnnotationDependencyMethodInjectionDemo {
private CommonInjectionEntity autowiredCommonInjectionEntity;
private CommonInjectionEntity resourceCommonInjectionEntity;
@Autowired
public void initAutowiredCommonInjectionEntity(CommonInjectionEntity autowiredCommonInjectionEntity) {
this.autowiredCommonInjectionEntity = autowiredCommonInjectionEntity;
}
@Resource
public void initResourceCommonInjectionEntity(CommonInjectionEntity resourceCommonInjectionEntity) {
this.resourceCommonInjectionEntity = resourceCommonInjectionEntity;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(AnnotationDependencyMethodInjectionDemo.class);
annotationConfigApplicationContext.refresh();
AnnotationDependencyMethodInjectionDemo bean = annotationConfigApplicationContext.getBean(AnnotationDependencyMethodInjectionDemo.class);
System.out.println("Autowired注入的值:"+bean.autowiredCommonInjectionEntity);
System.out.println("Resource注入的值:"+bean.resourceCommonInjectionEntity);
System.out.println("Autowired注入和Resource注入的值" + (bean.autowiredCommonInjectionEntity == bean.resourceCommonInjectionEntity));
annotationConfigApplicationContext.close();
}
@Bean
public CommonInjectionEntity commonInjectionEntity() {
return new CommonInjectionEntity(1L, "方法注入测试");
}
}
接口回调注入
Spring中提供了一个
Aware
,可以用于一些特定的Bean实例对象,比如BeanFactory
、ApplicationContext
注入等等。
接口名称 | 描述 |
BeanFactoryAware | 获取 IoC 容器 - BeanFactory |
ApplicationContextAware | 获取 Spring 应用上下文 - ApplicationContext 对象 |
EnvironmentAware | 获取 Environment 对象 |
ResourceLoaderAware | 获取资源加载器 对象 - ResourceLoader |
BeanClassLoaderAware | 获取加载当前 Bean Class 的 ClassLoader |
BeanNameAware | 获取当前 Bean 的名称 |
MessageSourceAware | 获取 MessageSource 对象,用于 Spring 国际化 |
ApplicationEventPublisherAware | 获取 ApplicationEventPublishAware 对象,用于 Spring 事件 |
EmbeddedValueResolverAware | 获取 StringValueResolver 对象,用于占位符处理 |
Java代码:
只演示了BeanFactoryAware
接口和ApplicationContextAware
接口。
public class DependencyAwareInjectionDemo implements BeanFactoryAware, ApplicationContextAware {
private static BeanFactory beanFactory;
private static ApplicationContext applicationContext;
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(DependencyAwareInjectionDemo.class);
annotationConfigApplicationContext.refresh();
System.out.println("BeanFactory注入对象是否相等:" + Objects.equals(beanFactory, annotationConfigApplicationContext.getBeanFactory()));
System.out.println("ApplicationContext注入对象是否相等:" + Objects.equals(applicationContext, annotationConfigApplicationContext));
annotationConfigApplicationContext.close();
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
DependencyAwareInjectionDemo.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
DependencyAwareInjectionDemo.applicationContext = applicationContext;
}
}
注入选型
各种注入方式没有绝对的好与坏,只有绝对的合理。所以我们在针对于注入选型的时候,更应该按照场景进行选择。
- • 低依赖: 构造器注入
- • 多依赖: Setter 方法注入
- • 便利性: 字段注入
- • 声明类: 方法注入
其他方式注入
除了常规的对象类型注入外,在Spring中还存在着很多其他的方式注入,比如基本类型注入、枚举注入、资源注入、集合注入以及限定注入等等。下面我们就讨论一下这些注入方式的使用。
基础类型注入
原生类型注入
原生类型主要指的就是基础类型,在Java中主要存在boolean
、byte
、char
、short
、int
、float
、long
、double
八大基础类型,基础类型注入比较简单,之前也有所涉及,直接输入值就可以了,这里就不进行演示了。
标量类型注入
Spring中标量注入的类型主要包括Number
、Character
、Boolean
、Enum
、Locale
、Charset
、Currency
、 Properties
、UUID
,下面我们对几种类型注入进行简单演示:
注入对象:
public enum SexEnum {
MAN,
WOMAN
}
public class ScalarInjectionEntity {
private Boolean aBoolean;
private SexEnum sex;
private Properties properties;
// ... 省略set、toString方法
}
XML配置:
<?xml versinotallow="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 https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="cn.phshi.domain.ScalarInjectionEntity">
<property name="aBoolean" value="true"/>
<property name="sex" value="MAN"/>
<property name="properties">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
<entry key="key3" value="value3"/>
</map>
</property>
</bean>
</beans>
Java代码:
public class DependencyScalarInjectionDemo {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-scalar-injection.xml");
ScalarInjectionEntity bean = beanFactory.getBean(ScalarInjectionEntity.class);
System.out.println(bean);
}
}
集合类型注入
集合一般可以看作是三大类,数组、Collection和map,即便Collection可以划分为更细的List、Set等等,map也可以划分为properties等等,当是他们的注入方式都是差不多的。
注入对象:
public class CollectionInjectionEntity {
private int[] arr;
private List<SexEnum> list;
private Map<String, SexEnum> map;
// ... 省略set、toString方法
}
XML配置:
<?xml versinotallow="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 https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="cn.phshi.domain.CollectionInjectionEntity">
<property name="arr" value="1,2,3"/>
<!-- <property name="list" value="MAN,WOMAN"/>-->
<!-- 或者 -->
<property name="list">
<list>
<value>MAN</value>
<value>WOMAN</value>
</list>
</property>
<property name="map">
<map key-type="java.lang.String" value-type="cn.phshi.domain.SexEnum">
<entry key="sex1" value="MAN"/>
<entry key="sex2" value="WOMAN"/>
<entry key="sex3" value="MAN"/>
</map>
</property>
</bean>
</beans>
Java代码:
public class DependencyCollectionInjectionDemo {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-collection-injection.xml");
CollectionInjectionEntity bean = beanFactory.getBean(CollectionInjectionEntity.class);
System.out.println(bean);
}
}
限定注入
限定注入主要指的是使用注解@Qualifier
或者他的派生进行分组注入。
- • @Qualifier 分组限定
Java代码:
public class DependencyQualifierInjectionDemo {
@Autowired
@Qualifier(value = "group1")
private Map<String,String> group1Str;
@Autowired
@Qualifier(value = "group2")
private Map<String,String> group2Str;
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(DependencyQualifierInjectionDemo.class);
annotationConfigApplicationContext.refresh();
DependencyQualifierInjectionDemo bean = annotationConfigApplicationContext.getBean(DependencyQualifierInjectionDemo.class);
System.out.println("分组1中的对象为:"+bean.group1Str);
System.out.println("分组2中的对象为:"+bean.group2Str);
annotationConfigApplicationContext.close();
}
@Bean
@Qualifier(value = "group1")
private String group1Str1() {
return "group1-str1";
}
@Bean
@Qualifier(value = "group1")
private String group1Str2() {
return "group1-str2";
}
@Bean
@Qualifier(value = "group2")
private String group2Str1() {
return "group2-str";
}
@Bean
@Qualifier(value = "group2")
private String group2Str2() {
return "group2-str2";
}
}
- • @Qualifie 派生注解分组限定
Java代码:
// 要打上标注@Qualifier
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface StrGroup {}
public class DependencyStrGroupInjectionDemo {
@Autowired
@StrGroup
private Map<String, String> strGroupStr;
@Autowired
@Qualifier
private Map<String, String> qualifierStr;
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(DependencyStrGroupInjectionDemo.class);
annotationConfigApplicationContext.refresh();
DependencyStrGroupInjectionDemo bean = annotationConfigApplicationContext.getBean(DependencyStrGroupInjectionDemo.class);
System.out.println("StrGroup分组中的内容为:" + bean.strGroupStr);
// Qualifier分组中会包含StrGroup分组中的内容
System.out.println("Qualifier分组的内容为:" + bean.qualifierStr);
annotationConfigApplicationContext.close();
}
@Bean
@StrGroup
private String group1Str1() {
return "strGroup-str1";
}
@Bean
@StrGroup
private String group1Str2() {
return "strGroup-str2";
}
@Bean
@Qualifier
private String group2Str1() {
return "qualifier-str1";
}
@Bean
@Qualifier
private String group2Str2() {
return "qualifier-str2";
}
}
Spring Cloud @LoadBalanced
便是使用这种方式进行限定注入:
image-20230321223606237
延迟依赖注入
延迟依赖注入在前面也说过很多,主要是采用ObjectFactory
及其派生对象ObjectProvider
来进行实现,注意,由于ObjectProvider
在实现了ObjectFactory
的同时,还实现了Iterable
,然后对Java8的函数式编程的支持,所以大多数情况下都是使用ObjectProvider
。
Java代码:
public class LazyDependencyInjectionDemo {
@Autowired
private ObjectFactory<Set<String>> objectFactoryStr;
// ObjectProvider实现了Iterable,所以注入集合更简单
@Autowired
private ObjectProvider<String> objectProvider;
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(LazyDependencyInjectionDemo.class);
annotationConfigApplicationContext.refresh();
LazyDependencyInjectionDemo bean = annotationConfigApplicationContext.getBean(LazyDependencyInjectionDemo.class);
System.out.println("objectFactoryStr中的内容为:");
bean.objectFactoryStr.getObject().forEach(System.out::printf);
System.out.println("\nobjectProvider的内容为:");
bean.objectProvider.forEach(System.out::printf);
annotationConfigApplicationContext.close();
}
@Bean
private String group1Str1() {
return "lazy-str1";
}
@Bean
private String group1Str2() {
return "lazy-str2";
}
}