目錄
注解方式
@ServletComponentScan注解
ServletComponentScanRegistrar類
ServletComponentRegisteringPostProcessor
handle()方法
配置類方式
ServletContextInitializerBeans
SpringBoot版本:2.1.1
注解方式
@ServletComponentScan注解
先了解下@ServletComponentScan注解。
@Import注解導入了一個類:ServletComponentScanRegistrar
三個屬性,也就是兩個屬性:
basePackages:String數組,指定要掃描的包
basePackageClasses:Class數組,指定掃描該類所在的包
如果沒有指定要掃描的包,則掃描主類所在的包,即@SpringBootApplication注解所在類的包。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2csYmRHFGaodVWvx2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL2UTM1AjM1YTM4IDNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
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的就說完了。