借鉴Spring ComponentScan组件扫描原理,自定义基于注解的Bean扫描注册组件,实现自定义Bean注册逻辑。
@ComponentScan注解能扫描指定路径下的标识了Spring Bean注解(@Component或者是@Component参与合成的注解,如@Service,@Configuration等)的类,将其注册为一个Spring容器内的一个Bean,Bean名称默认是类名首字母小写。
当然这个是Spring内的注册逻辑,但是最近项目需求以每个扫描到的Bean为基础,统一封装,生成额外的Bean,这个Bean是和公司平台的交互,项目部署后通过平台和用户进行交互,用户的每个请求均通过平台来访问,而不是直接访问应用。
平台封装Bean的逻辑和扫描注册有些区别,仅仅通过Spring扫描难以实现,如果通过手动注册的话,那么就需要额外的很多@Bean标识方法定义或者< bean id= … >形式配置,项目就变得很臃肿,重复性代码很多。
所以想通过借鉴@ComponentScan组件扫描的形式,扫描指定包下的类,所有标识了指定注解的Bean都把他们注册成平台交互的Bean。
首先,需要有一个能够标识类的注解,他的作用类似@Component或者其合成的注解:
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HsfComponent {
/**
* 是否要将标识此注解的类注册为Spring的Bean
*
* @return
*/
boolean registerBean() default false;
}
扫描包下的所有标识了这个注解的类都注册成平台交互的Bean,如果这个Bean没有在@ComponentScan的扫描包下,那么可以定义registerBean = true,可以在自定义组件中将其注册到Spring容器中。
其次需要一个类似@ComponentScan作用的扫描注解,它来指定扫描应用内哪些路径:
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(HsfBeanDefinitionRegistrar.class)
public @interface HsfComponentScan {
/**
* @return
*/
String[] value() default {};
/**
* 扫描包
*
* @return
*/
String[] basePackages() default {};
/**
* 扫描的基类
*
* @return
*/
Class>[] basePackageClasses() default {};
/**
* 包含过滤器
*
* @return
*/
Filter[] includeFilters() default {};
/**
* 排斥过滤器
*
* @return
*/
Filter[] excludeFilters() default {};
}
这个注解内的这几个属性定义我参考了@ComponentScan,@HsfComponentScan 注解的扫描包路径优先使用value(),其次basePackages(),再次是basePackageClasses(),如果这几个属性都未定义值,则使用标识了此注解的配置类的包路径作为扫描路径。includeFilters和excludeFilters是对扫描到的类作过滤,@ComponentScan定义时其属性useDefaultFilters默认值是true,也就是仅将那些标识了@Component及由@Component合成的那些注解时才算作一个Bean的类,所以我们也需要定义一个类型过滤器,将那些标识了@HsfComponent的类算作是平台交互Bean的基类:
/**
* 自定义HSF类型过滤,过滤抽象类,接口,注解,枚举,内部类及匿名类
*
* @create 2017-12-25 10:23
*/
public class HsfTypeFilter extends AbstractClassTestingTypeFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(HsfTypeFilter.class);
@Override
protected boolean match(ClassMetadata metadata) {
Class> clazz = transformToClass(metadata.getClassName());
if (clazz == null || !clazz.isAnnotationPresent(HsfComponent.class)) {
return false;
}
HsfComponent hsfComponent = clazz.getAnnotation(HsfComponent.class);
if (hsfComponent.registerBean() && isAnnotatedBySpring(clazz)) {
throw new IllegalStateException("类{" + clazz.getName() + "}已经标识了Spring组件注解,不能再指定[registerBean = true]");
}
//过滤抽象类,接口,注解,枚举,内部类及匿名类
return !metadata.isAbstract() && !clazz.isInterface() && !clazz.isAnnotation() && !clazz.isEnum()
&& !clazz.isMemberClass() && !clazz.getName().contains("$");
}
/**
* @param className
* @return
*/
private Class> transformToClass(String className) {
Class> clazz = null;
try {
clazz = ClassUtils.forName(className, this.getClass().getClassLoader());
} catch (ClassNotFoundException e) {
LOGGER.info("未找到指定HSF基础类{}", className);
}
return clazz;
}
/**
* @param clazz
* @return
*/
private boolean isAnnotatedBySpring(Class> clazz) {
return clazz.isAnnotationPresent(Component.class) || clazz.isAnnotationPresent(Configuration.class)
|| clazz.isAnnotationPresent(Service.class) || clazz.isAnnotationPresent(Repository.class)
|| clazz.isAnnotationPresent(Controller.class);
}
}
HsfTypeFilter 过滤那些不符合要求的类,比如@HsfComponent类内部类等,内部类在扫描时也是会扫描到的。
接下来的这个类时关键,它揭示了@HsfComponentScan的扫描和注册逻辑:
/**
* Hsf注册器
* 当HSF基类实现了多个接口时,切记将实际的业务接口放在第一个
*
* @create 2017-12-22 23:16
*/
public class HsfBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
private static final Logger LOGGER = LoggerFactory.getLogger(HsfBeanDefinitionRegistrar.class);
private static final String RESOURCE_PATTERN = "**/*.class";
//生成的HSF Bean名称到代理的Service Class的映射
private static final Map> HSF_UNDERLYING_MAPPING = new HashMap>();
/**
* @param importingClassMetadata
* @param registry
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annAttr = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(HsfComponentScan.class.getName()));
String[] basePackages = annAttr.getStringArray("value");
if (ObjectUtils.isEmpty(basePackages)) {
basePackages = annAttr.getStringArray("basePackages");
}
if (ObjectUtils.isEmpty(basePackages)) {
basePackages = getPackagesFromClasses(annAttr.getClassArray("basePackageClasses"));
}
if (ObjectUtils.isEmpty(basePackages)) {
basePackages = new String[] {ClassUtils.getPackageName(importingClassMetadata.getClassName())};
}
List includeFilters = extractTypeFilters(annAttr.getAnnotationArray("includeFilters"));
//增加一个包含的过滤器,扫描到的类只要不是抽象的,接口,枚举,注解,及匿名类那么就算是符合的
includeFilters.add(new HsfTypeFilter());
List excludeFilters = extractTypeFilters(annAttr.getAnnotationArray("excludeFilters"));
List> candidates = scanPackages(basePackages, includeFilters, excludeFilters);
if (candidates.isEmpty()) {
LOGGER.info("扫描指定HSF基础包[{}]时未发现复合条件的基础类", basePackages.toString());
return;
}
//注册HSF后处理器,为HSF对象注入环境配置信息
registerHsfBeanPostProcessor(registry);
//注册HSF
registerBeanDefinitions(candidates, registry);
}
/**
* @param basePackages
* @param includeFilters
* @param excludeFilters
* @return
*/
private List> scanPackages(String[] basePackages, List includeFilters, List excludeFilters) {
List> candidates = new ArrayList>();
for (String pkg : basePackages) {
try {
candidates.addAll(findCandidateClasses(pkg, includeFilters, excludeFilters));
} catch (IOException e) {
LOGGER.error("扫描指定HSF基础包[{}]时出现异常", pkg);
continue;
}
}
return candidates;
}
/**
* 获取符合要求的Controller名称
*
* @param basePackage
* @return
* @throws IOException
*/
private List> findCandidateClasses(String basePackage, List includeFilters, List excludeFilters) throws IOException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("开始扫描指定包{}下的所有类" + basePackage);
}
List> candidates = new ArrayList>();
String packageSearchPath = CLASSPATH_ALL_URL_PREFIX + replaceDotByDelimiter(basePackage) + '/' + RESOURCE_PATTERN;
ResourceLoader resourceLoader = new DefaultResourceLoader();
MetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(resourceLoader);
Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources(packageSearchPath);
for (Resource resource : resources) {
MetadataReader reader = readerFactory.getMetadataReader(resource);
if (isCandidateResource(reader, readerFactory, includeFilters, excludeFilters)) {
Class> candidateClass = transform(reader.getClassMetadata().getClassName());
if (candidateClass != null) {
candidates.add(candidateClass);
LOGGER.debug("扫描到符合要求HSF基础类:{}" + candidateClass.getName());
}
}
}
return candidates;
}
/**
* 注册HSF Bean,
* Bean的名称格式:
* 当内部Bean的类名类似FloorServiceImpl时 "HSFSpringProviderBean#FloorService"
* 如果内部Bean实现了多个接口,请将实际的业务接口放在第一位
*
* @param internalClasses
* @param registry
*/
private void registerBeanDefinitions(List> internalClasses, BeanDefinitionRegistry registry) {
for (Class> clazz : internalClasses) {
if (HSF_UNDERLYING_MAPPING.values().contains(clazz)) {
LOGGER.debug("重复扫描{}类,忽略重复注册", clazz.getName());
continue;
}
String beanName = generateHsfBeanName(clazz);
RootBeanDefinition rbd = new RootBeanDefinition(HSFSpringProviderBean.class);
registry.registerBeanDefinition(beanName, rbd);
if (registerSpringBean(clazz)) {
LOGGER.debug("注册HSF基础[{}]Bean", clazz.getName());
registry.registerBeanDefinition(ClassUtils.getShortNameAsProperty(clazz), new RootBeanDefinition(clazz));
}
HSF_UNDERLYING_MAPPING.put(beanName, clazz);
}
}
/**
* 注册HSF后处理器
*
* @param registry
*/
private void registerHsfBeanPostProcessor(BeanDefinitionRegistry registry) {
String beanName = ClassUtils.getShortNameAsProperty(HsfBeanPostProcessor.class);
if (!registry.containsBeanDefinition(beanName)) {
registry.registerBeanDefinition(beanName, new RootBeanDefinition(HsfBeanPostProcessor.class));
}
}
/**
* 当接口重名时,后注册的HSF Bean的名称后缀加上序号,从1开始,1代表第二个
*
* @param underlying
* @return
*/
private String generateHsfBeanName(Class> underlying) {
String interfaceName = underlying.getInterfaces()[0].getSimpleName();
String beanName = HSFSpringProviderBean.class.getSimpleName() + "#" + interfaceName;
if (HSF_UNDERLYING_MAPPING.containsKey(beanName)) {
beanName = beanName + "#" + getNextOrderSuffix(interfaceName);
}
return beanName;
}
/**
* 生成后注册的重名接口后缀
*
* @param className
* @return
*/
private Integer getNextOrderSuffix(String className) {
int order = 1;
for (String hsfBeanName : HSF_UNDERLYING_MAPPING.keySet()) {
if (hsfBeanName.substring(hsfBeanName.indexOf("#") + 1).startsWith(className)) {
String curOrder = hsfBeanName.substring((HSFSpringProviderBean.class.getSimpleName() + "#" + className).length());
if (!StringUtils.isEmpty(curOrder)) {
order = Math.max(Integer.valueOf(curOrder), order);
}
}
}
return order;
}
/**
* @param filterAttributes
* @return
*/
private List typeFiltersFor(AnnotationAttributes filterAttributes) {
List typeFilters = new ArrayList();
FilterType filterType = filterAttributes.getEnum("type");
for (Class> filterClass : filterAttributes.getClassArray("classes")) {
switch (filterType) {
case ANNOTATION:
Assert.isAssignable(Annotation.class, filterClass,
"@HsfComponentScan 注解类型的Filter必须指定一个注解");
Class annotationType = (Class)filterClass;
typeFilters.add(new AnnotationTypeFilter(annotationType));
break;
case ASSIGNABLE_TYPE:
typeFilters.add(new AssignableTypeFilter(filterClass));
break;
case CUSTOM:
Assert.isAssignable(TypeFilter.class, filterClass,
"@HsfComponentScan 自定义Filter必须实现TypeFilter接口");
TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class);
typeFilters.add(filter);
break;
default:
throw new IllegalArgumentException("当前TypeFilter不支持: " + filterType);
}
}
return typeFilters;
}
/**
* @param classes
* @return
*/
private String[] getPackagesFromClasses(Class[] classes) {
if (ObjectUtils.isEmpty(classes)) {
return null;
}
List basePackages = new ArrayList(classes.length);
for (Class> clazz : classes) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
return (String[])basePackages.toArray();
}
/**
* 用"/"替换包路径中"."
*
* @param path
* @return
*/
private String replaceDotByDelimiter(String path) {
return StringUtils.replace(path, ".", "/");
}
/**
* @param reader
* @param readerFactory
* @param includeFilters
* @param excludeFilters
* @return
* @throws IOException
*/
protected boolean isCandidateResource(MetadataReader reader, MetadataReaderFactory readerFactory, List includeFilters,
List excludeFilters) throws IOException {
for (TypeFilter tf : excludeFilters) {
if (tf.match(reader, readerFactory)) {
return false;
}
}
for (TypeFilter tf : includeFilters) {
if (tf.match(reader, readerFactory)) {
return true;
}
}
return false;
}
/**
* @param className
* @return
*/
private Class> transform(String className) {
Class> clazz = null;
try {
clazz = ClassUtils.forName(className, this.getClass().getClassLoader());
} catch (ClassNotFoundException e) {
LOGGER.info("未找到指定HSF基础类{%s}", className);
}
return clazz;
}
/**
* @param annAttrs
* @return
*/
private List extractTypeFilters(AnnotationAttributes[] annAttrs) {
List typeFilters = new ArrayList();
for (AnnotationAttributes filter : annAttrs) {
typeFilters.addAll(typeFiltersFor(filter));
}
return typeFilters;
}
/**
* @param beanClass
* @return
*/
private boolean registerSpringBean(Class> beanClass) {
return beanClass.getAnnotation(HsfComponent.class).registerBean();
}
/**
* @param hsfBeanName
* @return
*/
public static Class> getUnderlyingClass(String hsfBeanName) {
return HSF_UNDERLYING_MAPPING.get(hsfBeanName);
}
}
这个类扫描@HsfComponentScan内定义的包路径,将那些标识了@HsfComponent的复合要求的类注册为平台BeanDefinition,HSFSpringProviderBean类型。但是因为HSFSpringProviderBean的一些配置信息比如版本号等是在maven管理的Properties配置文件中,Spring容器只能在配置配置文件中才能得到,也就是在< bean id = … >这种类型下才能在项目编译时由maven替换获取,直接在java代码内不能直接获取这些配置信息的,所以HSFSpringProviderBean这个类型的Bean的一些属性由PostProcessor后处理器来注入。
因为平台Bean HSFSpringProviderBean的配置文件由maven管理,所以需要一个桥梁将maven的配置信息引入到Spring容器内,因此我们定义了一个Bean,配置在xml配置文件中,这样maven在编译时用配置值替换那些#{…}占位符号,这样在项目运行时我们就能通过代码获取到这些配置信息,将其注入到HSFSpringProviderBean中。
/**
* HSF通用属性配置
* 定义此类的目的是为了获取HSF的基本配置,原先配置是定义在antx.properties文件中,
* 这个文件由maven管理,在编译后不同环境下的value赋给相应的${...};
* 在项目运行时获取不到antx中的属性信息,因此不能通过代码从antx.properties中提取指定key的value;
* 至少需要定义一个Bean,由maven将当前环境的配置信息传入Spring容器中。
*
* @create 2017-12-25 13:42
*/
public class HsfGenericConfig {
private String serviceVersion;
private String serviceGroup;
private String clientTimeout;
public String getServiceVersion() {
return serviceVersion;
}
public void setServiceVersion(String serviceVersion) {
this.serviceVersion = serviceVersion;
}
public String getServiceGroup() {
return serviceGroup;
}
public void setServiceGroup(String serviceGroup) {
this.serviceGroup = serviceGroup;
}
public String getClientTimeout() {
return clientTimeout;
}
public void setClientTimeout(String clientTimeout) {
this.clientTimeout = clientTimeout;
}
}
HsfGenericConfig 的属性就是HSFSpringProviderBean的相应配置信息,Spring容器初始化后,HsfGenericConfig 就提取到了HSFSpringProviderBean的配置信息。
最后一个类就是将HsfGenericConfig 的配置信息拷贝到HSFSpringProviderBean类型的Bean中,完善平台Bean的初始化过程:
/**
* HSF后处理器
*
* @create 2017-12-25 10:51
*/
public class HsfBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements InitializingBean, BeanFactoryAware {
private BeanFactory beanFactory;
private HsfGenericConfig hsfGenericConfig;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
hsfGenericConfig = beanFactory.getBean(HsfGenericConfig.class);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof HSFSpringProviderBean && isScanedGeneratedHsfBean(beanName)) {
//拷贝配置信息
BeanUtils.copyProperties(hsfGenericConfig, bean);
Class> underlyingClass = HsfBeanDefinitionRegistrar.getUnderlyingClass(beanName);
Object underlyingBean = beanFactory.getBean(underlyingClass);
((HSFSpringProviderBean)bean).setTarget(underlyingBean);
((HSFSpringProviderBean)bean).setServiceInterface(underlyingClass.getInterfaces()[0].getName());
}
return bean;
}
/**
* 当前Bean是否是通过{@linkplain HsfComponentScan}注解式生成的Bean
*
* @param beanName
* @return
*/
private boolean isScanedGeneratedHsfBean(String beanName) {
return beanName.startsWith(HSFSpringProviderBean.class.getSimpleName()) && beanName.contains("#");
}
}
这个组件的类都介绍完了,总共6个,每个有各自的作用。
下面介绍使用方法:
总结:
HsfComponentScan类上用@Import()引入了HsfBeanDefinitionRegistrar类,而这个类是一个ImportBeanDefinitionRegistrar的实现类,Spring容器在解析ImportBeanDefinitionRegistrar类型的Bean时会调用其registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)方法,将@HsfComponentScan注解上的信息提取成AnnotationMetadata以及容器注册器对象作为此方法的参数,这个就是自定义注解式组件扫描的关键逻辑。