文章目录
前言
随着spring boot的流行,@Enable*设计模式渐渐兴起,通常一个注解就可以帮我们完成一个很使用功能。而在大多数的带有@Enable注解中,我们通常会看到 一个注解@Import去帮助我们导入一些类(bean)。个人觉得这是一个非常重要的spring ioc容器的扩展点之一,它可以让我们通过一个注解去给容器中注入很多bean,这也是spring boot中最常用、最基础的注解。
本文将主要介绍@Import、ImportSelector、ImportBeanDefinitionRegistrar的使用场景,以及源码分析他们注册bean的原理。(其实本文的源码分析原理再上一篇文章中已经讲到了,所以本文只会分析主要源码)。
1. 自定义注册bean之@Import的使用
单纯的使用@Import注解注入bean,这种用法比较少,最起码要配合类似于@Enable*注解类,来使用@Impor。比如通过一个@Enable的功能注解来控制某个功能的开关。
public class TestImport {
@Bean
public Person person(){
Person person = new Person();
person.setName("coyhzx");
return person;
}
}
同时在启动类上导入TestImport类
@Import(TestImport.class)
@Configuration
public class MyApp {}
#输出结果
person
com.upanda.app.test.Person@ef9296d
2. 自定义注册bean之ImportSelector的使用
ImportSelector是一个接口,接口中提供了一个方法selectImports。实现该方法,返回要导入的bean的名称数组,即可导入bean,importingClassMetadata是注解的元数据。
/**
* 选择并返回应基于导入@Configuration类的{@link AnnotationMetadata}导入的类的名称。
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
使用方法:
public class TestImportSelect implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.upanda.app.test.Person"};
}
#输出结果
person
com.upanda.app.test.Person@gd2296d
同时在启动类上导入TestImportSelect类,该导入方式与直接导入一个类的区别就是,它可以拿到一个注解的元数据以及它可以同时注册多个类,返回类名即可。比如说,我们可以根据注解里面的某个属性值类注入一个或倒戈bean。
3. 自定义注册bean之ImportBeanDefinitionRegistrar的使用
TestImportBeanDefinitionRegistrar是一个接口,目前有两个默认实现default方法,可以通过registerBeanDefinitions方法获取我们的容器,同时可以自定义创建bean以及向容器中注册bean的逻辑。
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName("com.upanda.app.test.Person");
beanDefinition.setScope("singleton");
registry.registerBeanDefinition("person",beanDefinition);
}
}
#输出结果
person
com.upanda.app.test.Person@da9296d
4. 自定义注册bean之spring经典实现–spring开启动态代理功能@EnableAspectJAutoProxy
sprinig framework开启aop动态代理功能,使用@EnableAspectJAutoProxy注解实现,而该注解使用@Import注解导入自定义的bean的这种方式是最经典、也是spring framework框架中内部使用的。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//是否使用cglib动态代理,默认使用jdk动态代理
boolean proxyTargetClass() default false;
//代理应由AOP框架公开为{@code ThreadLocal} ,以便通过{@link org.springframework.aop.framework.AopContext}类进行检索。 默认情况下为关闭,即不能保证{@code AopContext}访问将起作用
boolean exposeProxy() default false;
}
该注解会为我们导入AspectJAutoProxyRegistrar这个类,该类通过实现ImportBeanDefinitionRegistrar接口来自定义bean的注入逻辑。具体不涉及到AOP的核心功能,简单理解通过该方式注入了aop相关的后置处理器bean对象(后续aop源码中在具体讲解)。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@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);
}
}
}
}
5. 自定义Bean三种方式的源码解析
其实在上一篇文章@Configuration源码的解析中,以及提及到了@import注解在哪里进行处理了,这边继续进行上一章进行分析,侧重于@import的导入。
在ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法中parser.parse(candidates);解析配置类里,有一个处理import的方法processImports。
// Process any @Import annotations
//处理@Import注解,getImports(sourceClass)方法获取类上@Import导入的类
processImports(configClass, sourceClass, getImports(sourceClass), true);
方法getImports(sourceClass)获取到@import导入的类。
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
//1. 搜集Imports导入的类
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
//递归获取@Import注解导入的类
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
在配置类解析器ConfigurationClassParser类中的processImports方法里,该方法作用非常大。其作用我在源码中有注释,但是在这里也总结一下。
1.处理了@import导入的类,并将其加入到ConfigurationClassParser的配置类集合configurationClasses中。
2.处理实现了ImportSelector接口导入的类,递归处理selectImports中导入的类,最终也会加入到ConfigurationClassParser的配置类集合configurationClasses中。
3.处理实现ImportBeanDefinitionRegistrar接口导入的类,同时将该类直接放入ConfigurationClassParser配置类的importBeanDefinitionRegistrars集合中。
这里并没有注册bean,但是这里将@import、实现ImportSelector接口导入的类,以及实现ImportBeanDefinitionRegistrar接口的类,都放入了配置类的各种集合中,在后文中,注册配置类中就会将这些类全部注入到容器中。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
//2 处理ImportSelector,注册自定义的bean
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
//将读取到的类名数组,转换成SourceClass类数组
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
//递归处理导入的类,同时将其加入到解析类的集合中,也因为导入的类可能实现了ImportBeanDefinitionRegistrar接口 --默认导入的会最后的else中,当作普通的配置类进行处理
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
// 处理ImportBeanDefinitionRegistrar,注册自定义的bean
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
//3 将实现了ImportBeanDefinitionRegistrar接口的类,加入到配置类中的importBeanDefinitionRegistrars集合中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
//1. 处理@Import导入的既不是实现ImportSelector ,也不是实现ImportBeanDefinitionRegistrar的类
//且把它当作@Configuration类处理,也有一点递归调用
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
//将其加入到ConfigurationClassParser解析类的集合
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
processConfigurationClass这个递归处理配置类的方法中,会把每个配置类都加入到解析器的configurationClasses类中。
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
//重点:如果souurceClass不为空,则将器方法解析类的配置集合类中,后续会用到
this.configurationClasses.put(configClass, configClass);
}
真正注入bean的是在ConfigurationClassPostProcessor的this.reader.loadBeanDefinitions(configClasses)中。
//读取注册配置类(包括beanMethod、@import、实现importSelect、实现),并且注册到容器中
this.reader.loadBeanDefinitions(configClasses);
该reader为spring专门为配置类读取创建的一个类ConfigurationClassBeanDefinitionReader。reader.loadBeanDefinitions()方法会遍历加载所有的配置bean。至于在往下面,调用我们的容器为我们注入bean的逻辑相对而言很简单,有兴趣的可以打个断点看看,也可以参考我的spring源码分析的第一篇文章注入bean的流程图。
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
//为配置类注册bean的定义
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
//注册@import导入的类
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
//注册@Bean导入的类
loadBeanDefinitionsForBeanMethod(beanMethod);
}
//注册@ImportedResources导入的类
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
//注册ImportBeanDefinitionRegistrars
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
bean的定义以及特殊bean的注入、自定义注册源码解析了很多了,接下来可能就会开始spriing的DI在源码中是怎样实现的了。
请敬请期待我的下一篇文章~
上一篇:3、spring核心源码解析之@Configuration注解详解
下一篇:5、spring核心源码解析之DI依赖注入、自动装配@Autowired和@Resource