在特定的场景,我们需要将没有通过spring配置文件或注解的类加入到IOC容器中。这就需要我们了解spring如何将bean加载到IOC容器中,并对之功能进行扩展。
- 自定义类 的识别方式。 自定义注解?自定义包路径?
- ClassPathBeanDefinitionScanner 扫描器扩展
- ScannerConfigurer 交给客户端定义的配置

public class MyServiceScannerConfigurer implements BeanDefinitionRegistryPostProcessor,
InitializingBean, ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(RemoteServiceScannerConfigurer.class);
/** 要扫描的包 */
private String basePackage;
/** 排除自身项目包 */
private String excludePackage;
/** applicationContext上下文 通过实现ApplicationContextAware 获取 */
private ApplicationContext applicationContext;
/** beanName生成器 */
private BeanNameGenerator nameGenerator;
/** set方法指定要被扫描的包名 */
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
public BeanNameGenerator getNameGenerator() {
return nameGenerator;
}
public void setNameGenerator(BeanNameGenerator nameGenerator) {
this.nameGenerator = nameGenerator;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.basePackage, "Property 'basePackage' is required");
}
/** 实现BeanDefinitionRegistryPostProcessor接口 此方法中实例化 ClassPathRemoteServiceScanner 扫描器 */
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
LOGGER.info("BeanFactoryPostProcessor postProcessBeanDefinitionRegistry start...");
//实例化扫描器
MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry, excludePackage, applicationContext.getEnvironment());
//设置要扫描的注解
scanner.setAnnotationClass(RemoteService.class);
//传入applicationContext
scanner.setResourceLoader(this.applicationContext);
//传入beaanName生成器
scanner.setBeanNameGenerator(this.nameGenerator);
//过滤 不需要扫描的类
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// left intentionally blank
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setExcludePackage(String excludePackage) {
this.excludePackage = excludePackage;
}
}

public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
private static final String TEST_PROFILE = "test";
private static final Logger LOG = LoggerFactory.getLogger(ClassPathRemoteServiceScanner.class);
private Class<? extends Annotation> annotationClass;
private Class<?> markerInterface;
/**
* 排除自身项目包.
*/
private String excludePackage;
private RemoteServiceProxyFactoryBean<Object> exportServiceProxyFactoryBean = new RemoteServiceProxyFactoryBean<Object>();
public ClassPathRemoteServiceScanner(BeanDefinitionRegistry registry, String excludePackage, Environment environment) {
super(registry, true, environment);
this.excludePackage = excludePackage;
}
public ClassPathRemoteServiceScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
super(registry, useDefaultFilters);
}
public ClassPathRemoteServiceScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
super(registry, useDefaultFilters, environment);
}
public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
public void setMarkerInterface(Class<?> markerInterface) {
this.markerInterface = markerInterface;
}
/**
* Configures parent scanner to search for the right interfaces. It can search
* for all interfaces or just for those that extends a markerInterface or/and
* those annotated with the annotationClass
*/
public void registerFilters() {
boolean acceptAllInterfaces = true;
// if specified, use the given annotation and / or marker interface
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
// override AssignableTypeFilter to ignore matches on the actual marker interface
if (this.markerInterface != null) {
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
return false;
}
});
acceptAllInterfaces = false;
}
if (acceptAllInterfaces) {
// default include filter that accepts all classes
addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
}
// exclude package-info.java
addExcludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
if (excludePackage != null && className.startsWith(excludePackage)) {
return true;
}
return className.endsWith("package-info");
}
});
}
private void registerServiceMetadata(Class<?> clazz) {
for (Method method : clazz.getDeclaredMethods()) {
RemoteMethod annotation = method.getAnnotation(RemoteMethod.class);
if (annotation != null) {
String serviceName = annotation.serviceName();
//String serviceDesc = annotation.serviceDesc();
String methodName = method.getName();
RemoteType remoteType = annotation.remoteType();
String fullMethodName = clazz.getName() + "." + methodName;
if (!serviceName.isEmpty()) {
ServiceMetadata metadata = new ServiceMetadata();
metadata.setServiceName(serviceName);
metadata.setServiceMethod(method);
metadata.setRemoteType(remoteType);
metadata.setInterfaceName(fullMethodName);
metadata.setServiceInterface(clazz);
ServiceManager.getInstance().put(fullMethodName, metadata);
}
}
}
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOG.warn("No ExportService interface was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
LOG.info("Creating ExportServiceProxyFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
try {
Class<?> clazz = definition.resolveBeanClass(Thread.currentThread().getContextClassLoader());
RemoteService annotation = clazz.getAnnotation(RemoteService.class);
if (annotation != null) {
// the export service interface is the original class of the bean
// but, the actual class of the bean is ExportServiceProxyFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.exportServiceProxyFactoryBean.getClass());
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.getPropertyValues().add(TEST_PROFILE, getEnvironment().acceptsProfiles(TEST_PROFILE));
registerServiceMetadata(clazz);
}
} catch (ClassNotFoundException ignore) {
//ignore
LOG.error("", ignore);
}
}
}
@Override
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (super.checkCandidate(beanName, beanDefinition)) {
return true;
} else {
LOG.warn("Skipping MapperFactoryBean with name '" + beanName
+ "' and '" + beanDefinition.getBeanClassName() + "' mapperInterface"
+ ". Bean already defined with the same name!");
return false;
}
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
网友评论