快过年了感觉还是挺忙的,这两天把毕设论文写了写,然后想着学点别的,看了看尚硅谷的学习路线发现我有些学过去现在已经记不清了…本来想着看看哪些消息中间件之类的,发现基础有些还是没有掌握好,还是先补补基础吧……
开始看雷神的spring注解驱动,这里就整理以下讲到的注解之类的内容吧!
[TOC]
@ComponentScan
作用是自动扫描组件并可以指定扫描规则
点开ComponentScan 注解类查看源码:
如果需要在扫描时排除标注的类:
1
2
3
4
5
6
7
// value指定要扫描的包如果需要在扫描时只包含标注的类:
1
2
3
4
5
6
7
// value指定要扫描的包
重复注解
先来看看@ComponentScans注解,如下图所示。
在ComponentScans注解类的内部声明了一个返回ComponentScan[]数组的value()方法,所以我们可以在@ComponentScan注解中重复使用:
1 |
|
@Filter
使用@ComponentScan注解实现包扫描时,我们可以使用@Filter指定过滤规则
@Filter注解中的type属性是一个FilterType枚举,源码如下:
FilterType.ANNOTATION:按照注解进行包含或者排除
扫描只包含标注了@Controller注解的组件
1 |
|
FilterType.ASSIGNABLE_TYPE:按照给定的类型进行包含或者排除
扫描BookService这种类型的组件,如果是Java类,就包括子类 如果是接口,就包括实现类。
1 |
|
FilterType.ASPECTJ:按照ASPECTJ表达式进行包含或者排除
按照ASPECTJ表达式进行过滤(不常用)
1 |
|
FilterType.REGEX:按照正则表达式进行包含或者排除
按照正则表达式进行过滤(不常用)
1 |
|
FilterType.CUSTOM:按照自定义规则进行包含或者排除
如果实现自定义规则进行过滤时,自定义规则的类必须是org.springframework.core.type.filter.TypeFilter接口的实现类。
创建TypeFilter接口的实现类MyTypeFilter:
1
2
3
4
5
6
7
8
9
10
11
12public class MyTypeFilter implements TypeFilter {
/**
* 参数:
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类的信息的(工厂)
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return false; // 这儿我们先让其返回false
}
}当我们实现TypeFilter接口时,需要实现该接口中的match()方法,match()方法的返回值为boolean类型。
- 当返回true时,表示符合规则,会包含在Spring容器中;
- 当返回false时,表示不符合规则,不会被包含在Spring容器中。
使用@ComponentScan注解进行如下配置。
1
2
3
4
5
6
7
// value指定要扫描的包
实现自定义过滤规则
在MyTypeFilter类中打印出当前正在扫描的类名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class MyTypeFilter implements TypeFilter {
/**
* 参数:
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类的信息的(工厂)
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的类信息,比如说它的类型是什么,它实现了什么接口之类的
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类的资源信息,比如说类的路径等信息
Resource resource = metadataReader.getResource();
// 获取当前正在扫描的类的类名
String className = classMetadata.getClassName();
System.out.println("->" + className);
return false;
}
}在MainConfig类中配置自定义过滤规则:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//以前配置文件的方式被替换成了配置类,即配置类==配置文件
// 这个配置类也是一个组件
// 告诉Spring这是一个配置类
public class MainConfig {
// @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id
public Person person01() {
return new Person("lisi", 20);
}
}接着,运行IOCTest类中的test01()方法进行测试
1
2
3
4
5
6
7
8
9
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
// 我们现在就来看一下IOC容器中有哪些bean,即容器中所有bean定义的名字
String[] definitionNames = applicationContext.getBeanDefinitionNames();
for (String name : definitionNames) {
System.out.println(name);
}
}输出结果如下:
- 可以看到,已经输出了当前正在扫描的类的名称,同时,除了Spring内置的bean的名称之外,只输出了mainConfig和person,而没有输出使用@Repository、@Service、@Controller这些注解标注的组件的名称。
- 这是因为当前MainConfig类上标注的@ComponentScan注解是使用的自定义规则,而在自定义规则的实现类(即MyTypeFilter类)中,直接返回了false,那么就是一个都不匹配,所有的bean都没被包含进容器中。
我们可以在MyTypeFilter类中简单的实现一个规则,例如,当前扫描的类名称中包含有”er”字符串的,就返回true,否则就返回false。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class MyTypeFilter implements TypeFilter {
/**
* 参数:
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类的信息的(工厂)
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的类信息,比如说它的类型是什么啊,它实现了什么接口啊之类的
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类的资源信息,比如说类的路径等信息
Resource resource = metadataReader.getResource();
// 获取当前正在扫描的类的类名
String className = classMetadata.getClassName();
System.out.println("--->" + className);
// 现在来指定一个规则
if (className.contains("er")) {
return true; // 匹配成功,就会被包含在容器中
}
return false; // 匹配不成功,所有的bean都会被排除
}
}输出结果如下:
包括MyTypeFilter的原因是因为扫描的是com.zsxfa包,这个包下的每个类都会进入到这个自定义规则里面进行匹配,若匹配成功,则就会被包含在容器中。
@Scope
Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,并将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象。如果每次从Spring容器中获取对象时,都要创建一个新的实例对象,那么该如何处理呢?此时就需要使用@Scope注解来设置组件的作用域了。
@Scope注解原理
可以看到ConfigurableBeanFactory,点开ConfigurableBeanFactory的源码:
singleton代表单实例,prototype代表多实例
单实例Bean创建对象的时机
Spring容器在启动时,将单实例组件实例化之后,会即刻加载到Spring容器中,以后每次从容器中获取组件实例对象时,都是直接返回相应的对象,而不必再创建新的对象了。
多实例Bean创建对象的时机
当向Spring容器中获取Bean实例对象时,Spring容器才会实例化Bean对象,再将其加载到Spring容器中去。
每次向Spring容器获取对象时,它都会创建一个新的对象并返回。
单实例bean注意的事项
单实例bean是整个应用所共享的,所以需要考虑到线程安全问题,,SpringMVC中的Controller默认是单例的,有些开发者在Controller中创建了一些变量,那么这些变量实际上就变成共享的了,Controller又可能会被很多线程同时访问,这些线程并发去修改Controller中的共享变量,此时很有可能会出现数据错乱的问题
多实例bean注意的事项
多实例bean每次获取的时候都会重新创建,如果这个bean比较复杂,创建时间比较长,那么就会影响系统的性能
@Lazy-bean懒加载
Spring在启动时,默认会将单实例bean进行实例化,并加载到Spring容器中去。也就是说,单实例bean默认是在Spring容器启动的时候创建对象,并且还会将对象加载到Spring容器中。如果我们需要对某个bean进行延迟加载,那么该如何处理呢?此时,就需要使用到@Lazy注解了。
懒加载:
- 懒加载就是Spring容器启动的时候,先不创建对象,在第一次使用(获取)bean的时候再来创建对象,并进行一些初始化。
懒加载实现:
首先,我们在MainConfig2配置类中的person()方法上加上一个@Lazy注解,以此将Person对象设置为懒加载
1
2
3
4
5
6
7
8
9
public class MainConfig2 {
public Person person() {
System.out.println("给容器中添加这个Person对象...");
return new Person("marx", 25);
}
}运行IOCTest类中的test05()方法
发现没有输出注入bean时的语句
在IOCTest类中的test05()方法中获取一下Person对象
1
2
3
4
5
6
public void test05() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器创建完成");
Person person = (Person) applicationContext.getBean("person");
}再次运行:
验证是否创建了多个bean
1
2
3
4
5
6
7
8
public void test05() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器创建完成");
Person person = (Person) applicationContext.getBean("person");
Person person2 = (Person) applicationContext.getBean("person");
System.out.println(person == person2);
}说明没有多创建bean,实现了懒加载。
@Conditional
@Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean。
@Conditional注解是由Spring Framework提供的一个注解,它位于 org.springframework.context.annotation包内,定义如下。
从@Conditional注解的源码来看,@Conditional注解不仅可以添加到类上,也可以添加到方法上。在@Conditional注解中,还存在着一个Condition类型或者其子类型的Class对象数组,我们点进去看一下。
可以看到,它是一个接口。所以,我们使用@Conditional注解时,需要写一个类来实现Spring提供的Condition接口,它会匹配@Conditional所符合的方法,然后我们就可以使用我们在@Conditional注解中定义的类来检查了。
向Spring容器注册bean
在MainConfig2配置类中新增person01()方法和person02()方法,并为这两个方法添加@Bean注解
1 |
|
带条件注册bean
如果当前操作系统是Windows操作系统,那么就向Spring容器中注册名称为bill的Person对象;如果当前操作系统是Linux操作系统,那么就向Spring容器中注册名称为linus的Person对象。要想实现这个需求,我们就得要使用@Conditional注解了。
使用Spring中的AnnotationConfigApplicationContext类就能够获取到当前操作系统的类型
1 |
|
要想使用@Conditional注解,我们需要实现Condition接口来为@Conditional注解设置条件,所以,这里我们创建了两个实现Condition接口的类,它们分别是LinuxCondition和WindowsCondition,如下所示。
LinuxCondition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class LinuxCondition implements Condition {
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判断操作系统是否是Linux系统
// 1. 获取到bean的创建工厂(能获取到IOC容器使用到的BeanFactory,它就是创建对象以及进行装配的工厂)
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2. 获取到类加载器
ClassLoader classLoader = context.getClassLoader();
// 3. 获取当前环境信息,它里面就封装了我们这个当前运行时的一些信息,包括环境变量,以及包括虚拟机的一些变量
Environment environment = context.getEnvironment();
// 4. 获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
if (property.contains("linux")) {
return true;
}
return false;
}
}BeanDefinitionRegistry对象的源码:
Spring容器中所有的bean都可以通过BeanDefinitionRegistry对象来进行注册,因此我们可以通过它来查看Spring容器中到底注册了哪些bean。
因此,我们可以在这儿做更多的判断,比如说我可以判断一下Spring容器中是不是包含有某一个bean,就像下面这样,如果Spring容器中果真包含有名称为person的bean,那么就做些什么事情,如果没包含,那么我们还可以利用BeanDefinitionRegistry对象向Spring容器中注册一个bean。
WindowsCondition
1
2
3
4
5
6
7
8
9
10
11public class WindowsCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Windows")) {
return true;
}
return false;
}
}在MainConfig2配置类中使用@Conditional注解添加条件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainConfig2 {
public Person person() {
System.out.println("给容器中添加这个Person对象...");
return new Person("marx", 25);
}
public Person person01() {
return new Person("Bill Gates", 62);
}
public Person person02() {
return new Person("linus", 48);
}
}运行结果如下:没有名称为linus的bean了
@Conditional注解也可以标注在类上,标注在类上的含义是:只有满足了当前条件,这个配置类中配置的所有bean注册才能生效,也就是对配置类中的组件进行统一设置。
此时,我们在运行IOCTest类中的test06()方法时,设置一个
-Dos.name=linux
参数,就像下图所示的那样,这是我们将操作系统模拟为了linux系统。运行结果如下:可以看到,没有任何bean的定义信息输出,这是因为程序检测到了当前操作系统为linux,没有向Spring容器中注册任何bean的缘故导致的。
@Import
在项目中会经常引入一些第三方的类库,我们需要将这些第三方类库中的类注册到Spring容器中,此时,我们就可以使用@Bean和@Import注解将这些类快速的导入Spring容器中。
注册bean的方式
- 包扫描+给组件标注注解(@Controller、@Servcie、@Repository、@Component),但这种方式比较有局限性,局限于我们自己写的类
- @Bean注解,通常用于导入第三方包中的组件
- @Import注解,快速向Spring容器中导入一个组件
@Import注解源码
从源码里面可以看出@Import可以配合Configuration
、ImportSelector
以及ImportBeanDefinitionRegistrar
来使用,下面的or表示也可以把Import当成普通的bean来使用。
注意:@Import注解只允许放到类上面,不允许放到方法上。
@Import注解的使用方式
@Import注解的三种用法主要包括:
- 直接填写class数组的方式
- ImportSelector接口的方式,即批量导入,重点
- ImportBeanDefinitionRegistrar接口方式,即手工注册bean到容器中
1. @Import导入组件的示例
我们创建一个Color类,这个类是一个空类,没有成员变量和方法,如下所示。
1 | public class Color { |
在MainConfig2配置类上添加一个@Import注解,并将Color类填写到该注解中,如下所示。
1 | // 满足当前条件,这个类中配置的所有bean注册才能生效 |
运行IOCTest类中的testImport()方法,会发现输出的结果信息如下所示
可以看到,输出结果中打印了com.meimeixia.bean.Color,说明使用@Import注解快速地导入组件时,容器中就会自动注册这个组件,并且id默认是组件的全类名。
@Import注解还支持同时导入多个类
1 |
2. 使用ImportSelector
- ImportSelector源码:
该接口文档上说的明明白白,其主要作用是收集需要导入的配置类,selectImports()方法的返回值就是我们向Spring容器中导入的类的全类名。如果该接口的实现类同时实现EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware或者ResourceLoaderAware,那么在调用其selectImports()方法之前先调用上述接口中对应的方法,如果需要在所有的@Configuration处理完再导入时,那么可以实现DeferredImportSelector接口。
在ImportSelector接口的selectImports()方法中,存在一个AnnotationMetadata类型的参数,这个参数能够获取到当前标注@Import注解的类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息。
- ImportSelector实例
创建一个MyImportSelector类实现ImportSelector接口,如下所示,先在selectImports()方法中返回null
1 | public class MyImportSelector implements ImportSelector { |
在MainConfig2配置类的@Import注解中,导入MyImportSelector类,如下。
1 |
至于使用MyImportSelector类要导入哪些bean,就需要你在MyImportSelector类的selectImports()方法中进行设置了,只须在MyImportSelector类的selectImports()方法中返回要导入的类的全类名(包名+类名)即可。
接着,我们就要运行IOCTest类中的testImport()方法了,在运行该方法之前,咱们先在MyImportSelector类的selectImports()方法处打一个断点,debug调试一下,如下图所示。
打好断点之后,我们再以debug的方式来运行IOCTest类中的testImport()方法。
点击下一步,发现importClassName为null了,最后报错空指针异常。
因此要想不报这样一个空指针异常,咱们MyImportSelector类的selectImports()方法里面就不能返回一个null值了,不妨先返回一个空数组试试,就像下面这样。
1 | public class MyImportSelector implements ImportSelector { |
再次运行就不会报错了,正常输出结果。
创建两个Java类Bule类和Yellow类如下
1 | public class Bule { |
1 | public class Yellow { |
将以上两个类的全类名返回到MyImportSelector类的selectImports()方法中,此时,MyImportSelector类的selectImports()方法如下所示。
1 | public class MyImportSelector implements ImportSelector { |
输出结果如下:
3.ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar的源码:
由源码可以看出,ImportBeanDefinitionRegistrar本质上是一个接口。在ImportBeanDefinitionRegistrar接口中,有一个registerBeanDefinitions()方法,通过该方法,我们可以向Spring容器中注册bean实例。
Spring官方在动态注册bean时,大部分套路其实是使用ImportBeanDefinitionRegistrar接口。
所有实现了该接口的类都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化的,也能被aop、validator等机制处理。
使用方法
ImportBeanDefinitionRegistrar需要配合@Configuration和@Import这俩注解,其中,@Configuration注解定义Java格式的Spring配置文件,@Import注解导入实现了ImportBeanDefinitionRegistrar接口的类。ImportBeanDefinitionRegistrar接口实例
就创建一个MyImportBeanDefinitionRegistrar类,去实现ImportBeanDefinitionRegistrar接口
1
2
3
4
5
6
7
8
9
10
11
12
13public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类
*
* 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
} 在MainConfig2配置类上的@Import注解中,添加MyImportBeanDefinitionRegistrar类
1
// @Import快速地导入组件,id默认是组件的全类名
接着,创建一个RainBow类,作为测试ImportBeanDefinitionRegistrar接口的bean来使用,如下所示。
1
2public class RainBow {
}紧接着,我们就要实现MyImportBeanDefinitionRegistrar类中的registerBeanDefinitions()方法里面的逻辑了,添加逻辑后的registerBeanDefinitions()方法如下所示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类
*
* 我们可以通过调用BeanDefinitionRegistry接口中的registerBeanDefinition方法,手动注册所有需要添加到容器中的bean
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("com.zsxfa.bean.Yellow");
boolean definition2 = registry.containsBeanDefinition("com.zsxfa.bean.Bule");
if (definition && definition2) {
// 指定bean的定义信息,包括bean的类型、作用域等等
// RootBeanDefinition是BeanDefinition接口的一个实现类
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); // bean的定义信息
// 注册一个bean,并且指定bean的名称
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}以上registerBeanDefinitions()方法的实现逻辑很简单,就是判断Spring容器中是否同时存在以
com.zsxfa.bean.Yellow
命名的bean和以com.zsxfa.bean.Bule
命名的bean,如果真的同时存在,那么向Spring容器中注入一个以rainBow命名的bean。运行IOCTest类中的testImport()方法来进行测试,输出结果信息如下所示。
使用FactoryBean向Spring容器中注册bean
我们可以通过多种方式向Spring容器中注册bean。可以使用@Configuration注解结合@Bean注解向Spring容器中注册bean;可以按照条件向Spring容器中注册bean;可以使用@Import注解向容器中快速导入bean对象;可以在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean。
FactoryBean概述
一般情况下,Spring是通过反射机制利用bean的class属性指定实现类来实例化bean的。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,那么则需要在标签中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可以得到一个更加简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。
FactoryBean接口对于Spring框架来说占有非常重要的地位,Spring自身就提供了70多个FactoryBean接口的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean
在Spring 4.3.12.RELEASE
这个版本中,FactoryBean接口的定义如下所示。
这里,需要注意的是:当配置文件中标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
FactoryBean实例
首先,创建一个ColorFactoryBean类,它得实现FactoryBean接口,如下所示。
1 | public class ColorFactoryBean implements FactoryBean<Color> { |
然后,我们在MainConfig2配置类中加入ColorFactoryBean的声明,如下所示。
1 |
|
这里需要注意的是:我在这里使用@Bean注解向Spring容器中注册的是ColorFactoryBean对象。
那现在我们就来看看Spring容器中到底都有哪些bean。我们所要做的事情就是,运行IOCTest类中的testImport()方法,此时,输出的结果信息如下所示。
可以看到,结果信息中输出了一个colorFactoryBean!此时,我们对IOCTest类中的testImport()方法稍加改动,添加获取colorFactoryBean的代码,并输出colorFactoryBean实例的类型,如下所示。
1 |
|
再次运行IOCTest类中的testImport()方法,发现输出的结果信息如下所示。
可以看到,虽然我在代码中使用@Bean注解注入的是ColorFactoryBean对象,但是实际上从Spring容器中获取到的bean对象却是调用ColorFactoryBean类中的getObject()方法获取到的Color对象。
在ColorFactoryBean类中,我们将Color对象设置为单实例bean,即让isSingleton()方法返回true。接下来,我们在IOCTest类中的testImport()方法里面多次获取Color对象,并判断一下多次获取的对象是否为同一对象,如下所示。
1 |
|
然后,运行IOCTest类中的testImport()方法,此时输出的结果信息如下所示。
可以看到,在ColorFactoryBean类中的isSingleton()方法里面返回true时,每次获取到的Color对象都是同一个对象,说明Color对象是单实例bean。
如何在Spring容器中获取到FactoryBean对象本身?
只需要在获取工厂Bean本身时,在id前面加上&符号即可,例如&colorFactoryBean。
打开我们的IOCTest测试类,在testImport()方法中添加获取ColorFactoryBean实例的代码,如下所示。
1 |
|
此时,运行以上testImport()方法,会发现输出的结果信息如下所示。
为什么在id前面加上&符号就会获取到ColorFactoryBean实例对象呢?
打开BeanFactory接口,查看其源码。
在BeanFactory接口中定义了一个&前缀,只要我们使用bean的id来从Spring容器中获取bean时,Spring就会知道我们是在获取FactoryBean本身。