天天看點

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

目錄

注解方式

@ServletComponentScan注解

ServletComponentScanRegistrar類

ServletComponentRegisteringPostProcessor

handle()方法

配置類方式

ServletContextInitializerBeans 

SpringBoot版本:2.1.1 

注解方式

@ServletComponentScan注解

先了解下@ServletComponentScan注解。

@Import注解導入了一個類:ServletComponentScanRegistrar

三個屬性,也就是兩個屬性:

basePackages:String數組,指定要掃描的包

basePackageClasses:Class數組,指定掃描該類所在的包

如果沒有指定要掃描的包,則掃描主類所在的包,即@SpringBootApplication注解所在類的包。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

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。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式
SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

 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是由子類構造方法中傳遞過來的。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

 在doHandle()方法中就是建立bean定義,然後從注解擷取屬性值添加,最後注冊。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式
SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

上面整個流程就是使用注解的方式來使用Servlet,Filter和listener,注冊bean是在重新整理上下文執行invokeBeanFactoryPostProcessors()方法的過程中調用ServletComponentRegisteringPostProcessor的postProcessBeanFactory()方法實作自動注冊。

配置類方式

 先看下面這張類圖,頂層接口是ServletContextInitializer。該接口隻有一個方法void onStartup(ServletContext servletContext);在容器啟動過程中調用。與WebApplicationInitializer不同,SpringServletContainerInitializer不會檢測到實作此接口(且不實作WebApplicationInitializer)的類,是以不會由servlet容器自動引導。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

 通過ServletWebServerFactory工廠建立webServer,預設是TomcatServletWebServerFactory。是以得到的webServer也是TomcatWebServer。具體TomcatWebServer建立過程不詳細介紹,截圖展示一下。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式
SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式
SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式
SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

在這裡調用。StandardContext的startInternal()方法中。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

 這裡主要看selfInitialize方法中建立ServletContextInitializerBeans對象的過程。循環裡onStartup()方法就是将Servlet,Filter和listener添加到ServletContext中。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

ServletContextInitializerBeans 

 建立ServletContextInitializerBeans對象,該對象父類繼承Collection;主要是看該類構造方法中執行的操作。

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式
@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這個方法。 

SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式
SpringBoot(十九)Servlet,Filter和listener自動裝配解析注解方式配置類方式

以配置類的方式大緻就是上面這個流程,看到網上有人讨論這兩種方式使用,filter的執行順序,如果沒指定order值,那麼預設值就是Integer.MAX_VALUE,這個可以到AnnotationAwareOrderComparator的父類OrderComparator中的getOrder()方法去看。

兩種方使用方式是如何實作自動注入bean的就說完了。

繼續閱讀