简单来说,此注解就是指定扫描路径的,通过value
、basePackages
或者basePackageClasses
。主要还是看下ServletComponentScanRegistrar
类,这才是关键。
class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar { private static final String BEAN_NAME = "servletComponentRegisteringPostProcessor"; @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 获取包路径
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata); // 若已注册,则更新,否则新增
if (registry.containsBeanDefinition(BEAN_NAME)) {
updatePostProcessor(registry, packagesToScan);
} else {
addPostProcessor(registry, packagesToScan);
}
} private void updatePostProcessor(BeanDefinitionRegistry registry, Set<String> packagesToScan) {
BeanDefinition definition = registry.getBeanDefinition(BEAN_NAME);
ValueHolder constructorArguments = definition.getConstructorArgumentValues().getGenericArgumentValue(Set.class); @SuppressWarnings("unchecked")
Set<String> mergedPackages = (Set<String>) constructorArguments.getValue();
mergedPackages.addAll(packagesToScan);
constructorArguments.setValue(mergedPackages);
} private void addPostProcessor(BeanDefinitionRegistry registry, Set<String> packagesToScan) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); // 设置类
beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class); // 设置构造函数参数
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(packagesToScan);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
} private Set<String> getPackagesToScan(AnnotationMetadata metadata) { // 获取注解ServletComponentScan的属性信息
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(ServletComponentScan.class.getName())); // 获取属性basePackages和basePackageClasses
String[] basePackages = attributes.getStringArray("basePackages");
Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
Set<String> packagesToScan = new LinkedHashSet<String>();
packagesToScan.addAll(Arrays.asList(basePackages)); // basePackageClasses 最后也是根据basePackageClasses来获取塔对应的包路径
for (Class<?> basePackageClass : basePackageClasses) {
packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
} // 默认不填写时,获取的是被注解类所在包路径,所以一般放在启动类上
if (packagesToScan.isEmpty()) {
packagesToScan.add(ClassUtils.getPackageName(metadata.getClassName()));
} return packagesToScan;
}
}
可以看见,它是一个ImportBeanDefinitionRegistrar
的实现类,ImportBeanDefinitionRegistrar
可以动态地装载Bean
。再来看看ServletComponentRegisteringPostProcessor
类,此类是个BeanFactoryPostProcessor
,BeanFactory的后置处理器,简单理解就是扩展点吧。启动的时候会调用postProcessBeanFactory
方法。
ServletComponentRegisteringPostProcessor
源码就不贴了,简单来说,它的作用就是:扫描被@WebServlet
、@WebFilter
及@WebListener
的类,最后通过对应的ServletRegistrationBean
、FilterRegistrationBean
及ServletListenerRegistrationBean
进行注册。看见这些是不是很熟悉了。
//部分代码
static {
List<ServletComponentHandler> servletComponentHandlers = new ArrayList<ServletComponentHandler>();
servletComponentHandlers.add(new WebServletHandler());
servletComponentHandlers.add(new WebFilterHandler());
servletComponentHandlers.add(new WebListenerHandler());
HANDLERS = Collections.unmodifiableList(servletComponentHandlers);
}
关键看这个方法scanPackage
:
private void scanPackage(
ClassPathScanningCandidateComponentProvider componentProvider,
String packageToScan) { for (BeanDefinition candidate : componentProvider
.findCandidateComponents(packageToScan)) { if (candidate instanceof ScannedGenericBeanDefinition) { for (ServletComponentHandler handler : HANDLERS) {
handler.handle(((ScannedGenericBeanDefinition) candidate),
(BeanDefinitionRegistry) this.applicationContext);
}
}
}
}
可以看见,通过componentProvider.findCandidateComponents(packageToScan)
方法获取到对应的注解类,同时判断是否为以上说的三种,最后调用其doHandle
方法完成注册功能。以下是WebFilterHandler
的doHandler
方法。

现在,我们看看findCandidateComponents
方法怎么获取对应注解类的。

断点之后,可以看见是AnnotationConfigEmbeddedWebApplicationContext
类,

继续断点进去,最后是使用PathMatchingResourcePatternResolver
类进行资源获取的。

通过递归的方式,获取所有的类:

最后关键就是这个Arrays.sort(dirContents)
了。所以简单来说,可以通过class类名来达到排序效果。但这种方案要限制类名,还是使用FilterRegistrationBean
之类的来设置吧。
总结
写的可能有点乱也有点水,⊙﹏⊙‖∣。主要还是想纠正下原先的错误,O__O…。知其然知其所以然,还有很长的路要走。没有写里面的细节,只是大致讲解了下。有兴趣的可以自行跟踪看看。
最后
目前互联网上很多大佬都有SpringBoot
系列教程,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。
原文出处:https://www.cnblogs.com/okong/p/correct-webfilter.html