摘要
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 {
}
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>
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 {
}
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>
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>
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>
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>
2
3
4
5
6
何时使用构造注入?何时使用setter注入?
强制依赖用构造,可选依赖用Setter。在settter方法上使用 @Required
注解即可令属性强制依赖。
# 2.3 静态工厂的方法注入
通过静态工厂获取实例,注入Spring中
public ItemFactory {
// 静态工厂方法
public static Item getItem() {
return new Item();
}
}
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>
2
3
4
5
6
静态工厂方法如何传递参数?
官方文档建议静态工厂方法的参数,应该通过 constructor-arg
元素产生,就像是bean的构造函数一样。
# 2.3 实例工厂的方法注入
获取对象实例的方法不是静态的,所以需要首先new工厂类,再调用普通的实例方法:
public ItemFactory {
// 普通工厂方法
public Item getItem() {
return new Item();
}
}
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>
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 {
}
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);
}
}
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 {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Import
的 value
值是一个数组,例如:@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 {}
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) {
}
}
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);
}
}
}
}
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);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
这样就把 Teacher
类注入到 Spring
容器中了
# 3.3 FactoryBean
提到 FactoryBean,就不得不与 BeanFactory 比较一番。
- BeanFactory:是
Factory
,IOC
容器或者对象工厂,所有的 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;
}
}
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();
}
}
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;
}
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;
}
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