目錄
ServletComponentScanRegistrar類
ServletComponentRegisteringPostProcessor
ServletContextInitializerBeans
SpringBoot版本:2.1.1
註解方式
@ServletComponentScan註解
先了解下@ServletComponentScan註解。
@Import註解導入了一個類:ServletComponentScanRegistrar
三個屬性,也就是兩個屬性:
basePackages:String數組,指定要掃描的包
basePackageClasses:Class數組,指定掃描該類所在的包
如果沒有指定要掃描的包,則掃描主類所在的包,即@SpringBootApplication註解所在類的包。
ServletComponentScanRegistrar類
該類主要作用是確定要掃描的包,並註冊一個BeanFactoryPostProcessor,用於掃描@WebServlet、@WebFilter和@WebListener註解,自動註冊成bean。
class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
private static final String BEAN_NAME = "servletComponentRegisteringPostProcessor";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
//得到註解中指定的包,如果沒有則掃描主類所在的包
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
//判斷bean工廠中是否存在servletComponentRegisteringPostProcessor的bean定義
if (registry.containsBeanDefinition(BEAN_NAME)) {
//存在的話直接從servletComponentRegisteringPostProcessor的構造參數中得到要掃描的包,加上從註解中得到的指定包
updatePostProcessor(registry, packagesToScan);
}
else {
//不存在就註冊一個
addPostProcessor(registry, packagesToScan);
}
}
private void updatePostProcessor(BeanDefinitionRegistry registry,
Set<String> packagesToScan) {
//得到bean定義
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) {
//通用bean定義
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
//設置class
beanDefinition.setBeanClass(ServletComponentRegisteringPostProcessor.class);
//設置構造方法參數
beanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(packagesToScan);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//註冊
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
}
private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
//得到AnnotationAttributes 對象,包含了@ServletComponentScan的屬性值
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(ServletComponentScan.class.getName()));
//得到basePackages的值
String[] basePackages = attributes.getStringArray("basePackages");
//得到basePackageClasses的值
Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
Set<String> packagesToScan = new LinkedHashSet<>();
packagesToScan.addAll(Arrays.asList(basePackages));
for (Class<?> basePackageClass : basePackageClasses) {
//得到指定類所在的包
packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
}
//如果爲空
if (packagesToScan.isEmpty()) {
//這裏取到的包名,就是主類的包名
packagesToScan.add(ClassUtils.getPackageName(metadata.getClassName()));
}
return packagesToScan;
}
}
ServletComponentRegisteringPostProcessor
上面說的構造方法中的值即要掃描的包,靜態代碼塊往List中添加了三個handler,分別對應處理@WebServlet、@WebFilter和@WebListener。
postProcessBeanFactory()方法會在刷新上下文的時候進行調用,前面說過了,不詳細介紹在哪調用。
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
//是否web應用
if (isRunningInEmbeddedWebServer()) {
//該對象主要是根據過濾規則掃描候選項
ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();
for (String packageToScan : this.packagesToScan) {
scanPackage(componentProvider, packageToScan);
}
}
}
private void scanPackage(
ClassPathScanningCandidateComponentProvider componentProvider,
String packageToScan) {
//根據過濾規則返回候選項,循環
for (BeanDefinition candidate : componentProvider
.findCandidateComponents(packageToScan)) {
if (candidate instanceof ScannedGenericBeanDefinition) {
for (ServletComponentHandler handler : HANDLERS) {
//傳入bean定義,已經beanRegistry
handler.handle(((ScannedGenericBeanDefinition) candidate),
(BeanDefinitionRegistry) this.applicationContext);
}
}
}
}
private boolean isRunningInEmbeddedWebServer() {
return this.applicationContext instanceof WebApplicationContext
&& ((WebApplicationContext) this.applicationContext)
.getServletContext() == null;
}
private ClassPathScanningCandidateComponentProvider createComponentProvider() {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
//設置環境
componentProvider.setEnvironment(this.applicationContext.getEnvironment());
//設置resourceLoader
componentProvider.setResourceLoader(this.applicationContext);
for (ServletComponentHandler handler : HANDLERS) {
//添加過濾規則,即上面講的三個註解,在啓動流程中創建上下文說過的
componentProvider.addIncludeFilter(handler.getTypeFilter());
}
return componentProvider;
}
handle()方法
該方法在父類ServletComponentHandler中。attributes見的應該很多了,doHandle()方法是個抽象方法,由子類實現,下面就只拿一個講。annotationType是由子類構造方法中傳遞過來的。
在doHandle()方法中就是創建bean定義,然後從註解獲取屬性值添加,最後註冊。
上面整個流程就是使用註解的方式來使用Servlet,Filter和listener,註冊bean是在刷新上下文執行invokeBeanFactoryPostProcessors()方法的過程中調用ServletComponentRegisteringPostProcessor的postProcessBeanFactory()方法實現自動註冊。
配置類方式
先看下面這張類圖,頂層接口是ServletContextInitializer。該接口只有一個方法void onStartup(ServletContext servletContext);在容器啓動過程中調用。與WebApplicationInitializer不同,SpringServletContainerInitializer不會檢測到實現此接口(且不實現WebApplicationInitializer)的類,因此不會由servlet容器自動引導。
通過ServletWebServerFactory工廠創建webServer,默認是TomcatServletWebServerFactory。所以得到的webServer也是TomcatWebServer。具體TomcatWebServer創建過程不詳細介紹,截圖展示一下。
在這裏調用。StandardContext的startInternal()方法中。
這裏主要看selfInitialize方法中創建ServletContextInitializerBeans對象的過程。循環裏onStartup()方法就是將Servlet,Filter和listener添加到ServletContext中。
ServletContextInitializerBeans
創建ServletContextInitializerBeans對象,該對象父類繼承Collection;主要是看該類構造方法中執行的操作。
@SafeVarargs
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
//不爲空就是傳進來的type,爲空就是ServletContextInitializer
this.initializerTypes = (initializerTypes.length != 0)
? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
//這裏就會從bean工廠得到ServletContextInitializer類型的beanName,然後根據name得到bean
//根據類型添加到MultiValueMap中
addServletContextInitializerBeans(beanFactory);
//設置order,默認就是Integer的最大值
addAdaptableBeans(beanFactory);
//根據order排序
List<ServletContextInitializer> sortedInitializers = this.initializers.values()
.stream()
.flatMap((value) -> value.stream()
.sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
//不可變List
this.sortedList = Collections.unmodifiableList(sortedInitializers);
//debug日誌
logMappings(this.initializers);
}
這裏主要看getOrderedBeansOfType這個方法。
以配置類的方式大致就是上面這個流程,看到網上有人討論這兩種方式使用,filter的執行順序,如果沒指定order值,那麼默認值就是Integer.MAX_VALUE,這個可以到AnnotationAwareOrderComparator的父類OrderComparator中的getOrder()方法去看。
兩種方使用方式是如何實現自動注入bean的就說完了。