案例:使用Spring的BeanFactoryPostProcessor扩展点完成自定义注解扫描
要求如下
- 自定义
@MyComponent
注解,使用在类上 - 使用提供的包扫描工具
BaseClassScanUtils
完成指定包的扫描 - 自定义
BeanFactoryPostProcessor
完成注解@MyComponent
的解析,解析后最终被Spring管理
1. 自定义@MyComponent注解
MyComponent.java
代码如下:
package com.xxx.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
String value();
}
2. 包扫描工具
BaseClassScanUtils.java
代码如下
package com.xxx.utils;
import com.xxx.anno.MyComponent;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.util.HashMap;
import java.util.Map;
public class BaseClassScanUtils {
//设置资源规则
private static final String RESOURCE_PATTERN = "/**/*.class";
public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {
//创建容器存储使用了指定注解的Bean字节码对象
Map<String, Class> annotationClassMap = new HashMap<String, Class>();
//spring工具类,可以获取指定路径下的全部类
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
//MetadataReader 的工厂类
MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
//用于读取类信息
MetadataReader reader = refractory.getMetadataReader(resource);
//扫描到的class
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
//判断是否属于指定的注解类型
if (clazz.isAnnotationPresent(MyComponent.class)) {
//获得注解对象
MyComponent annotation = clazz.getAnnotation(MyComponent.class);
//获得属value属性值
String beanName = annotation.value();
//判断是否为""
if (!beanName.equals("")) {
//存储到Map中去
annotationClassMap.put(beanName, clazz);
continue;
}
//如果没有为"",那就把当前类的类名作为beanName
annotationClassMap.put(clazz.getSimpleName(), clazz);
}
}
} catch (Exception exception) {
exception.printStackTrace();
exception.getCause();
System.err.println(exception.getMessage());
}
return annotationClassMap;
}
//测试
public static void main(String[] args) {
Map<String, Class> stringClassMap = scanMyComponentAnnotation("com.xxxx.lln");
System.out.println(stringClassMap);
}
}
3. 自定义BeanFactoryPostProcessor
完成注解@MyComponent
的解析
MyComponentBeanFactoryPostProcessor.java
代码如下
package com.xxx.factory;
import com.xxx.utils.BaseClassScanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import java.util.Map;
public class MyComponentBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
String basePackage = "com.xxx";
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//通过扫描工具去扫描指定包及其子包下的所有类,收集使用@MyComponent注解的类
Map<String, Class> myComponentAnnotationMap = BaseClassScanUtils.scanMyComponentAnnotation(basePackage);
//循环遍历map,组装BeanDefinition进行注册
myComponentAnnotationMap.forEach((beanName, clazz) -> {
//获取beanClassName
String beanClassName = clazz.getName();
// 创建BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(beanClassName);
//注册
beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
});
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
4. 将 MyComponentBeanFactoryPostProcessor
交由Spring容器进行管理
<!--
配置 Spring 后处理器
-->
<bean id="myComponentBeanFactoryPostProcessor" class="com.xxx.factory.MyComponentBeanFactoryPostProcessor"/>
5. 创建测试用的Bean对象
PersonDao.java
代码如下:
package com.xxx.dao;
public interface PersonDao {
}
package com.xxx.dao.impl;
import com.xxx.anno.MyComponent;
import com.xxx.dao.PersonDao;
@MyComponent(value = "PersonDao")
public class PersonDaoImpl implements PersonDao {
public PersonDaoImpl() {
System.out.println("PersonDaoImpl 实例化成功 ... ");
}
@Override
public String toString() {
return "PersonDaoImpl{}";
}
}
6. 结果测试
ApplicationContextTest.java
代码如下:
package com.xxx;
import com.alibaba.druid.pool.DruidDataSource;
import com.xxx.dao.PersonDao;
import com.xxx.service.UserService;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.sql.Connection;
import java.text.SimpleDateFormat;
public class ApplicationContextTest {
@Test
/**
* 测试 Spring 后处理器 MyComponentBeanFactoryPostProcessor
* 使用注解注册Bean
*/
public void test01() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
PersonDao personDao = applicationContext.getBean(PersonDao.class);
System.out.println(personDao);
}
}
控制台输出的测试结果
7. 总结
上述截图显示,我们已经成功的使用自定义的注解进行 Bean 的配置。可以直接在需要交由Spring容器管理的Bean对象上添加 @MyComponent
注解即可完成对 Bean 对象的配置,不在需要使用配置文件进行配置。