Spring 注入

5/9/2022 JavaSpring

摘要

JDK:1.8.0_202
Spring Version:5.2.11.RELEASE

# 一:前言

IOC 即控制反转,也称为依赖注入,是指将对象的创建或者依赖关系的引用从具体的对象控制转为框架或者 IOC 容器来完成,也就是依赖对象的获得被反转了。

可以简单理解为原来由我们来创建对象,现在由Spring来创建并控制对象。

# 二:XML

它的注入方式分为:set方法注入、构造器注入、工厂方法注入。

xml方式存在的缺点如下:

  • xml文件配置起来比较麻烦,既要维护代码又要维护配置文件,开发效率低;
  • 项目中配置文件过多,维护起来比较困难;
  • 程序编译期间无法对配置项的正确性进行验证,只能在运行期发现并且出错之后不易排查;
  • 解析 xml 时,无论是将 xml 一次性装进内存,还是一行一行解析,都会占用内存资源,影响性能。

# 2.1 setter注入

public Order {
	// 要注入的对象
	private Item item1;
	
	public void setItem1(Item item) {
		this.item1 = item;
	}
}

public Item {
}
1
2
3
4
5
6
7
8
9
10
11

编写Spring的XML文件:

<bean id="item" class="top.qform.Item" />
<bean id="order" class="top.qform.Order">
	<property name="item1" ref="item" />
</bean>
1
2
3
4

只和注入方法名有关,和属性名无关,例如上面的 setItem1 方法名改为 setITem1 将注入失败。

# 2.2 构造器注入

public Order {
	// 要注入的对象
	private Item item1;
	private String name;
	private String code;
	
	public Order(String name, Item item1, String code) {
		this.name = name;
		this.item1 = item1;
		this.code = code;
	}
}

public Item {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

编写Spring的XML文件:

<bean id="item" class="top.qform.Item" />
<bean id="order" class="top.qform.Order">
	<constructor-arg ref="item" />
	<constructor-arg value="饮料" />
	<constructor-arg value="编号" />
</bean>
1
2
3
4
5
6

<bean id="item" class="top.qform.Item" />
<bean id="order" class="top.qform.Order">
	<constructor-arg type="top.qform.Item" ref="item" />
	<constructor-arg type="String" value="饮料" />
	<constructor-arg type="String" value="编号" />
</bean>
1
2
3
4
5
6

Spring会按照类型排序,同类型按先后顺序向构造函数参数赋值。所以建议按照构造函数的参数顺序编写。也可以指定形参名,如下:

<bean id="item" class="top.qform.Item" />
<bean id="order" class="top.qform.Order">
	<constructor-arg name="item" ref="item" />
	<constructor-arg name="name" value="饮料" />
	<constructor-arg name="code" value="编号" />
</bean>
1
2
3
4
5
6

指定的索引顺序一定要和类型匹配,不然会报错

<bean id="item" class="top.qform.Item" />
<bean id="order" class="top.qform.Order">
	<constructor-arg index="1" name="item" ref="item" />
	<constructor-arg index="0" value="饮料" />
	<constructor-arg index="2" name="code" value="编号" />
</bean>
1
2
3
4
5
6

何时使用构造注入?何时使用setter注入?
强制依赖用构造,可选依赖用Setter。在settter方法上使用 @Required 注解即可令属性强制依赖。

# 2.3 静态工厂的方法注入

通过静态工厂获取实例,注入Spring中

public ItemFactory {

	// 静态工厂方法
	public static Item getItem() {
		return new Item();
	}
	
}
1
2
3
4
5
6
7
8

编写Spring的XML文件:

<bean id="item" class="top.qform.ItemFactory" factory-method="getItem" />
<bean id="order" class="top.qform.Order">
	<constructor-arg name="item" ref="item" />
	<constructor-arg name="name" value="饮料" />
	<constructor-arg name="code" value="编号" />
</bean>
1
2
3
4
5
6

静态工厂方法如何传递参数? 官方文档建议静态工厂方法的参数,应该通过 constructor-arg 元素产生,就像是bean的构造函数一样。

# 2.3 实例工厂的方法注入

获取对象实例的方法不是静态的,所以需要首先new工厂类,再调用普通的实例方法:

public ItemFactory {

	// 普通工厂方法
	public Item getItem() {
		return new Item();
	}
	
}
1
2
3
4
5
6
7
8

编写Spring的XML文件:

<bean id="itemFactory" class="top.qform.ItemFactory" />
<bean id="item" factory-bean="itemFactory" factory-method="getItem" />
<bean id="order" class="top.qform.Order">
	<constructor-arg name="item" ref="item" />
	<constructor-arg name="name" value="饮料" />
	<constructor-arg name="code" value="编号" />
</bean>
1
2
3
4
5
6
7

# 三:注解方式

# 3.1 @Configuration + @Bean

  • @Configuration:用来声明一个配置类,可以理解为 xml<beans> 标签。

  • @Bean:用来声明一个 bean,将其加入到 Spring 容器中,可以理解为 xml<bean> 标签。

public Order {
	// 要注入的对象
	private Item item1;
	
	public void setItem1(Item item) {
		this.item1 = item;
	}
}

public Item {
}
1
2
3
4
5
6
7
8
9
10
11

将上面两个类,注入到 Spring

@Configuration
public class BeanConfig {
	
	@Bean
	public Item getItem() {
		return new Item();
	}
	
	// 待补充
	@Bean
	public Order getOrder(Item item) {
		return new Order(item);
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 3.2 @Import

在翻看Spring源码的过程中,经常会看到 @Import 注解,它也可以用来将第三方 jar 包注入Spring,但是它只可以作用在上。

Swagger2 里的 EnableSwagger2 上就包含了 @Import 注解,用于将 Swagger2DocumentationConfiguration 配置文件加载进 Spring 容器

package springfox.documentation.swagger2.annotations;

import org.springframework.context.annotation.Import;
import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({Swagger2DocumentationConfiguration.class})
public @interface EnableSwagger2 {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Importvalue 值是一个数组,例如:@Import({Demo1.class, Demo2.class, Demo3.class}),一个一个注入比较繁琐,因此可以搭配 ImportSelector 接口来使用,例如:

public class MyImportSelector implements ImportSelector {
 @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"top.qform.Teacher", "top.qform.Student"};
    }
}

@Configuration
@Import(MyImportSelector.class)
public class MyConfig {}
1
2
3
4
5
6
7
8
9
10

其中 selectImports 方法返回的数组就会通过 @Import 注解注入到 Spring 容器中。

无独有偶,ImportBeanDefinitionRegistrar 接口也提供了注入 bean 的方法。

package org.springframework.context.annotation;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.type.AnnotationMetadata;

/**
 * 
 * // 待完善
 *
 */
public interface ImportBeanDefinitionRegistrar {

	/**
	 * 根据 @Configuration 注释给定元数据,根据需要注册 bean 定义。
	 *
	 * 请注意,由于与 @Configuration 类处理相关的生命周期限制,此处可能未注册 BeanDefinitionRegistryPostProcessor 类型。
	 * 
	 * 默认实现委托给 registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry)。
	 * 
	 * @param importingClassMetadata   annotation metadata of the importing class
	 * @param registry   current bean definition registry
	 * @param importBeanNameGenerator   导入 bean 的 bean 名称生成器策略:
	 * 		默认情况下为 ConfigurationClassPostProcessor.IMPORT_BEAN_NAME_GENERATOR,
	 * 		如果已设置 ConfigurationClassPostProcessor.setBeanNameGenerator,
	 * 		则为用户提供的策略。在后一种情况下,传入的策略将与包含应用程序上下文中的组件扫描所使用的策略相同
	 * 		(否则,默认的组件扫描命名策略是 AnnotationBeanNameGenerator.INSTANCE)
	 * 		
	 * @since 5.2
	 */ 
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {

		registerBeanDefinitions(importingClassMetadata, registry);
	}
	
	/**
	 * 根据 @Configuration 注释给定元数据,根据需要注册 bean 定义。
	 *
	 * 请注意,由于与 @Configuration 类处理相关的生命周期限制,此处可能未注册 BeanDefinitionRegistryPostProcessor 类型。
	 * 
	 * 默认实现为空。
	 * 
	 * @param importingClassMetadata   annotation metadata of the importing class
	 * @param registry   current bean definition registry
	 */ 
	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}

}
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

其中 AspectJAutoProxyRegistrar 类,发现它实现了 ImportBeanDefinitionRegistrar 接口,它的 registerBeanDefinitions 方法便是注入 bean 的过程。

package org.springframework.context.annotation;

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

/**
 * 根据导入的 @Configuration 类上的@EnableAspectJAutoProxy.proxyTargetClass() 属性的值注册、升级和配置AspectJ 自动代理创建者。
 */
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * 根据导入的 @Configuration 类上的@EnableAspectJAutoProxy.proxyTargetClass() 属性的值注册、升级和配置AspectJ 自动代理创建者。
	 */
	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}
	
}
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

如果觉得源代码比较难懂,可以看一下下面这个自定义的类

@Configuration
@Import(value = {MyImportBeanDefinitionRegistrar.class})
public class MyConfig {}

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            RootBeanDefinition tDefinition = new RootBeanDefinition(Teacher.class);
            // 注册 Bean,并指定bean的名称和类型
            registry.registerBeanDefinition("teacher", tDefinition);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

这样就把 Teacher 类注入到 Spring容器中了

# 3.3 FactoryBean

提到 FactoryBean,就不得不与 BeanFactory 比较一番。

  • BeanFactory:是 FactoryIOC 容器或者对象工厂,所有的 Bean 都由它进行管理。
  • FactoryBean:是 Bean,是一个能产生或者修饰对象生成的工厂 Bean,实现与工厂模式和修饰器模式类似。

那么 FactoryBean 是如何实现 bean 注入的呢?

先定义实现了 FactoryBean 接口的类

public class TeacherFactoryBean implements FactoryBean<Teacher> {

    /**
     * 返回此工厂管理的对象实例
     **/
    @Override
    public Teacher getObject() throws Exception {
		return new Teacher();
    }

    /**
     * 返回此 FactoryBean 创建的对象的类型
     **/
    @Override
    public Class<?> getObjectType() {
		return Teacher.class;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

然后通过 @Configuration + @Bean 的方式将 TeacherFactoryBean 加入到容器中

@Configuration
public class MyConfig {
	@Bean
	public TeacherFactoryBean teacherFactoryBean(){
		return new TeacherFactoryBean();
	}
}
1
2
3
4
5
6
7

注意:没有直接向容器中注入 Teacher,而是直接注入的 TeacherFactoryBean,然后从容器中拿 Teacher 这个类型的 bean

# 3.4 BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor源码

package org.springframework.beans.factory.support;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
	// 注册bean到spring容器中
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}
1
2
3
4
5
6
7
8
9

BeanFactoryPostProcessor源码

package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;

/**
 * 
 *
 * @since 06.07.2003
 * @see BeanPostProcessor
 * @see PropertyResourceConfigurer
 */
public interface BeanFactoryPostProcessor {

	/**
	 * 
	 * @param beanFactory the bean factory used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

// 待完善

https://developer.huawei.com/consumer/cn/forum/topic/0201780611934560379?fid=0101592429757310384

https://www.cnblogs.com/tanghaorong/p/14115432.html

https://www.cnblogs.com/wuchanming/p/5426746.html

https://blog.csdn.net/echizao1839/article/details/88063013

https://blog.csdn.net/icarus_wang/article/details/51649635

# 四:参考文献

最后更新: 5/17/2022, 3:10:53 PM