Spring Boot 2.1.1(六)基于ImportSelector接口和@Import注解自定义注解实现Bean装配

前言

上一篇博客讲了@EnableAutoConfiguration注解的工作原理,在上篇博客的基础上自定义注解,实现Bean的装配。

准备工作

在SpringBoot_EnableAutoConfiguration项目中新建两个bean,ImportBean、UserBean。里面啥都没有,什么也不用加,建好就行了。再建个配置类ImportConfiguration。如下:

@Configuration
public class ImportConfiguration {
	@Bean
	public UserBean userBean() {
		return new UserBean();
	}
	
	@Bean
	public MyRunnable myRunnable() {
		return new MyRunnable();
	}
}

EnableFlag(装配bean)

创建注解EnableFlag,如下(那些元注解我就不解释了,问度娘)。

/**
 * 自定义注解,装配bean
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface EnableFlag {
	
	/**
	 * bean的完全限定名称
	 * @return
	 */
	String[] strName() default {};
	
	/**
	 * bean的class对象
	 * @return
	 */
	Class<?>[] value() default {};
}

上一篇@EnableAutoConfiguration工作原理讲了一个ImportSelector接口,方法返回的是一个Class全路径的String数组,返回的Class会被Spring容器管理。所以注解的实现关键就在这了,创建一个类EnableFlag_ImportSelector实现ImportSelector接口,如下。

EnableFlag_ImportSelector:

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;

import com.eastcom.annotation.EnableFlag;

public class EnableFlag_ImportSelector implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		System.out.println("=========获取的注解信息>>"+importingClassMetadata.getAnnotationAttributes(EnableFlag.class.getName()));
		return StringUtils.toStringArray(getList(importingClassMetadata));
	}
	
	private List<String> getList(AnnotationMetadata importingClassMetadata) {
		AnnotationAttributes attributes = getAttributes(importingClassMetadata);
		List<String> list = getAutoConfigurationList(importingClassMetadata,attributes);
		System.out.println("===========返回的list>>"+list);
		return list;
	}
	/**
	 * 
	 * getAnnotationAttributes(String annotationName,boolean classValuesAsString):检索给定类型的注释的属性
	 * annotationName:要查找的注释类型的完全限定类名
	 * classValuesAsString:是否将类引用转换为String类名,以便在返回的Map中将值作为值转换,而不是可能必须首先加载的类引用
	 * 
	 * fromMap(Map<String,Object>):根据给定的Map返回AnnotationAttributes实例。
	 * @return
	 */
	private AnnotationAttributes getAttributes(AnnotationMetadata annotationMetadata){
		String name = EnableFlag.class.getName();
		AnnotationAttributes attributes = AnnotationAttributes
				.fromMap(annotationMetadata.getAnnotationAttributes(name, true));
		return attributes;
	}
	/**
	 * 获取注解属性值
	 * @param metadata
	 * @param attributes
	 * @return
	 */
	private List<String> getAutoConfigurationList(AnnotationMetadata metadata,
			AnnotationAttributes attributes){
		List<String> data = new LinkedList<>();
		data.addAll(Arrays.asList(attributes.getStringArray("strName")));
		data.addAll(asList(attributes, "value"));
		return data;
	}
	
	protected final List<String> asList(AnnotationAttributes attributes, String name) {
		String[] value = attributes.getStringArray(name);
		return Arrays.asList((value != null) ? value : new String[0]);
	}
}

我相信看过上篇博客的应该能看懂这个类里的方法都干了些什么。然后用@Import注解导入EnableFlag_ImportSelector,修改EnableFlag注解,如下。

/**
 * 自定义注解,装配bean
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableFlag_ImportSelector.class)
public @interface EnableFlag {
	
	/**
	 * bean的完全限定名称
	 * @return
	 */
	String[] strName() default {};
	
	/**
	 * bean的class对象
	 * @return
	 */
	Class<?>[] value() default {};
}

测试运行:

CustomAnnotationApp:

@EnableFlag(value={ImportBean.class},strName="com.eastcom.configuration.ImportConfiguration")
@SpringBootApplication
public class CustomAnnotationApp {
	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(CustomAnnotationApp.class, args);
		System.out.println(context.getBean(ImportBean.class));
		System.out.println(context.getBean("importConfiguration"));
		System.out.println(context.getBean(UserBean.class));
	}
}

结果:

Spring Boot 2.1.1(六)基于ImportSelector接口和@Import注解自定义注解实现Bean装配

参数里的ImportBean和ImportConfiguration配置的bean以及ImportConfiguration本身都成功装配。

项目源码https://github.com/AiHePing/SpringBoot_Demo