关于Spring的编译可以看我之前写的文章,该文展示了在Mac下编译Spring 5的流程
本文内容主要基于Spring的注解开发
如何将Bean导入容器
首先准备一个POJO:
public class User {
private String name;
public User(String name) {
this.name = name;
}
}
接着,一般使用Spring我们需要准备一份配置文件,但是基于注解开发就不需要了。只要准备一个注解配置类即可:
@Configuration
public class BeanConfig {
@Bean
public User user(){
return new User("Q");
}
}
将配置类传入AnnotationConfigApplicationContext类:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
这样,我们就获得了Spring的容器抽象接口ApplicationContext。
想要从容器中获取被Spring接管的Bean,我们可以调用ApplicationContext#getBean
Object user = applicationContext.getBean("user");
好样的,恭喜你成功已经学会了Spring框架的使用。
@Bean
通过第一个例子可以看出,在@Configuration
注解标注的配置类中可以通过@Bean
注解标注方法进而将方法返回值导入容器。
@ComponentScan
批量导入,@ComponentScan
注解也是一个不错的选择,它可以将指定包下的所有@Component
@Service
@Repository
@Controller
注解标注的类加入容器,这里就不多说了。
@Import
@Import
注解主要提供配置类导入的功能。
准备一个配置类:
@Configuration
public class ForImport {
@Bean
public User user(){
return new User("Q");
}
}
使用@Import
导入该配置:
@Import(ForImport.class)
public class ImportConfig {
}
测试:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportConfig.class);
Object user = applicationContext.getBean("user");
System.out.println(user);
结果:
User{name='Q'}
这只是其中一种用法。
@Import
注解还可以直接将普通类作为Bean导入到容器。
准备一个类:
public class Dog {
}
修改之前的ImportConfig类:
@Import({ForImport.class, Dog.class})
public class ImportConfig {
}
测试:
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportConfig.class);
printBeans(applicationContext);
}
public static void printBeans(ApplicationContext applicationContext) {
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
importConfig
top.hellooooo.blog.config.ForImport
user
top.hellooooo.blog.bean.Dog
可以看到,通过@Import
导入的类作为Bean在容器中的名字是类的全限定路径加上类名。
你以为这就是@Import
的全部功能了吗?Too young,too naive
打开Spring关于Import注解的接口源码
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class>[] value();
除去Configuration
,还有ImportSelector
以及ImportBeanDefinitionRegistrar
。
ImportSelector
ImportSelector
接口有两个方法,我们看第一个:
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
* @return the class names, or an empty array if none
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
一目了然,将返回的字符串代表的类导入。
下面实现我们自己的ImportSelector
:
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"top.hellooooo.blog.bean.Dog"};
}
}
修改ImportConfig
:
@Import({ForImport.class, MyImportSelector.class})
public class ImportConfig {
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
importConfig
top.hellooooo.blog.config.ForImport
user
top.hellooooo.blog.bean.Dog
ImportBeanDefinitionRegistrar
Import
注解源码里出现过ImportBeanDefinitionRegistrar,一看这名字就懂了,肯定是直接将BeanDefinition
导入容器。
这是ImportBeanDefinitionRegistrar
的一个默认方法
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
AnnotationMetadata接口可以获取@Import
类上的所有注解信息,BeanDefinitionRegistry接口则用来注册Bean。
来一个例子:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Set annotationTypes = importingClassMetadata.getAnnotationTypes();
for (String annotationType : annotationTypes) {
System.out.println("---->" + annotationType);
}
BeanDefinition dog = new RootBeanDefinition(Dog.class);
registry.registerBeanDefinition("dog", dog);
}
}
同样,修改ImportConfig
:
@Import({ForImport.class, MyImportBeanDefinitionRegistrar.class})
public class ImportConfig {
}
结果:
---->org.springframework.context.annotation.Import
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
importConfig
top.hellooooo.blog.config.ForImport
user
dog
嗯,不错。
不过,针对复杂类的导入,Spring还提供了FactoryBean
接口,我们来看看。
FactoryBean
FactoryBean
总共含有3个方法:
public Cat getObject() throws Exception;
public Class> getObjectType();
default boolean isSingleton() {
return true;
}
getObject返回实例(返回此工厂创建的对象的实例。实例是否可以共享,具体取决于该工厂是否返回单例或原型。),getObjectType返回由getObject()方法返回的对象类型;如果类型未知,则返回null。
就不举例了,获取FactoryBean生成的Bean和普通Bean一样,getBean即可,如果要获取FactoryBean本身的话,传入getBean的字符串前加上"&"即可。
"&"在BeanFactory中被定义:
String FACTORY_BEAN_PREFIX = "&";
参考
- Spring——The IoC Container
- Spring源码解析