Springboot注冊(cè)Servlet幾種方式你都知道??jī)?nèi)部實(shí)現(xiàn)原理解析
環(huán)境:springboot2.3.9.RELEASE
1 Servlet注冊(cè)
方式1:
在配置類(lèi)(啟動(dòng)類(lèi))上添加@ServletComponentScan注解
- @SpringBootApplication
 - @ServletComponentScan
 - public class SpringBootComprehensiveApplication
 - }
 
Servlet類(lèi)上添加@WebServlet注解接口
- @WebServlet("/hello")
 - public class MyServlet extends HttpServlet {
 - }
 
對(duì)應(yīng)的Filter, Linstener有:@WebFilter, @WebListener
方式2:
通過(guò)向IOC容器添加ServletRegistrationBean方式;該種方式可以在Servlet中注入其它Bean或者讀取application.properties配置信息等。對(duì)應(yīng)的filter, Listener有對(duì)應(yīng)的bean;FilterRegistrationBean,ServletListenerRegistrationBean
- @Bean
 - public ServletRegistrationBean<MyServlet> servlet() {
 - ServletRegistrationBean<MyServlet> servlet = new ServletRegistrationBean<>(new MyServlet()) ;
 - servlet.addUrlMappings("/hello") ;
 - return servlet ;
 - }
 
方式3:
動(dòng)態(tài)注冊(cè)Servlet
- @Component
 - public class DynamicRegServlet implements ServletContextInitializer {
 - @Override
 - public void onStartup(ServletContext servletContext) throws ServletException {
 - ServletRegistration initServlet = servletContext.addServlet("myServlet", MyServlet.class) ;
 - initServlet.addMapping("/hello") ;
 - }
 - }
 
該種方式是利用的Servlet3.0開(kāi)始才有的功能,通過(guò)SPI技術(shù)在容器啟動(dòng)的時(shí)候做一些初始化工作,比如注冊(cè)Servlet等。在Servle規(guī)范中通過(guò)
ServletContainerInitializer實(shí)現(xiàn)該功能。
該Servlet規(guī)范的開(kāi)發(fā)流程如下;
2、配置
ServletContainerInitializer
在src/META-INF/services下新建
javax.servlet.ServletContainerInitializer文件,文件內(nèi)容為ServletContainerInitializer接口的實(shí)現(xiàn)類(lèi)(完整的包名+類(lèi)名)如下:
- com.pack.container.config.CustomServletInitializer
 
CustomServletInitializer類(lèi)
- @HandlesTypes({ScoreWebInit.class})
 - public class CustomServletInitializer implements ServletContainerInitializer {
 - @Override
 - public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
 - c.forEach(web -> {
 - try {
 - ((ScoreWebInit)web.newInstance()).pay() ;
 - } catch (InstantiationException e) {
 - e.printStackTrace();
 - } catch (IllegalAccessException e) {
 - e.printStackTrace();
 - }
 - });
 - ServletRegistration.Dynamic reg = ctx.addServlet("MyServlet", com.pack.servlet.MyServlet.class) ;
 - reg.setLoadOnStartup(1) ;
 - reg.addMapping("/hello") ;
 - }
 - }
 
注意:@HandlesTypes該注解會(huì)把屬性value配置的值(ScoreWebInt.class)對(duì)應(yīng)的所有類(lèi)都收集上然后在onStartup方法中的Set
在spring-web-xxxx.jar下就有通過(guò)該技術(shù)實(shí)現(xiàn)的相應(yīng)功能。
3 掃描Servlet實(shí)現(xiàn)原理
在方式1中的實(shí)現(xiàn)原理就是掃描類(lèi)路徑下所有@WebServlet,@WebFilter,@WebListener。找到所有的類(lèi)后再通過(guò)方式2的方式進(jìn)行注冊(cè)。下面將核心源碼貼出
3.1 導(dǎo)入核心類(lèi)
- // 該注解如果沒(méi)有配置basePackages或者basePackageClasses屬性,那么會(huì)讀取當(dāng)前添加@ServletComponentScan注解的類(lèi)的包路徑進(jìn)行掃描。
 - @Target(ElementType.TYPE)
 - @Retention(RetentionPolicy.RUNTIME)
 - @Documented
 - @Import(ServletComponentScanRegistrar.class)
 - public @interface ServletComponentScan {
 - }
 - // 注冊(cè)BeanDefinition
 - class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar{
 - public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
 - Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
 - if (registry.containsBeanDefinition(BEAN_NAME)) {
 - updatePostProcessor(registry, packagesToScan);
 - } else {
 - // 當(dāng)前容器中沒(méi)有對(duì)應(yīng)的Bean時(shí)執(zhí)行該方法
 - addPostProcessor(registry, packagesToScan);
 - }
 - }
 - }
 
3.2 注冊(cè)BeanFactory 處理器
ServletComponentScanRegistrar最核心的功能就是注冊(cè)BeanFactoryPostProcessor
- class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar{
 - 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);
 - }
 - }
 
3.3 實(shí)例化掃描組件
進(jìn)入
ServletComponentRegisteringPostProcessor類(lèi)中首先這個(gè)類(lèi)有個(gè)static代碼塊
- class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
 - private static final List<ServletComponentHandler> HANDLERS;
 - static {
 - List<ServletComponentHandler> servletComponentHandlers = new ArrayList<>();
 - servletComponentHandlers.add(new WebServletHandler());
 - servletComponentHandlers.add(new WebFilterHandler());
 - servletComponentHandlers.add(new WebListenerHandler());
 - HANDLERS = Collections.unmodifiableList(servletComponentHandlers);
 - }
 - }
 
這段代碼分別是添加相應(yīng)Servlet, Filter, Listener的處理句柄,分別處理@WebServlet,@WebFilter,@WebListener 注解。
查看WebServletHandler
- class WebServletHandler extends ServletComponentHandler {
 - WebServletHandler() {
 - super(WebServlet.class);
 - }
 - // 看到該方法也就十分清楚了最終找到所有的Class以后,通過(guò)ServletRegistrationBean進(jìn)行注冊(cè)為Bean
 - @Override
 - public void doHandle(Map<String, Object> attributes, AnnotatedBeanDefinition beanDefinition,BeanDefinitionRegistry registry) {
 - BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServletRegistrationBean.class);
 - builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));
 - builder.addPropertyValue("initParameters", extractInitParameters(attributes));
 - builder.addPropertyValue("loadOnStartup", attributes.get("loadOnStartup"));
 - String name = determineName(attributes, beanDefinition);
 - builder.addPropertyValue("name", name);
 - builder.addPropertyValue("servlet", beanDefinition);
 - builder.addPropertyValue("urlMappings", extractUrlPatterns(attributes));
 - builder.addPropertyValue("multipartConfig", determineMultipartConfig(beanDefinition));
 - registry.registerBeanDefinition(name, builder.getBeanDefinition());
 - }
 - // other code
 - }
 - // 父類(lèi)ServletComponentHandler;在父類(lèi)總添加相應(yīng)的過(guò)濾器(分別查找相應(yīng)注解的類(lèi),@WebServlet等。)
 - abstract class ServletComponentHandler {
 - private final Class<? extends Annotation> annotationType;
 - private final TypeFilter typeFilter;
 - protected ServletComponentHandler(Class<? extends Annotation> annotationType) {
 - this.typeFilter = new AnnotationTypeFilter(annotationType);
 - this.annotationType = annotationType;
 - }
 - }
 
接下來(lái)執(zhí)行BeanFactoryPostProcessor對(duì)應(yīng)的回調(diào)方法了
- class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
 - @Override
 - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
 - if (isRunningInEmbeddedWebServer()) {
 - ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();
 - for (String packageToScan : this.packagesToScan) {
 - scanPackage(componentProvider, packageToScan);
 - }
 - }
 - }
 - }
 
createComponentProvider方法進(jìn)行創(chuàng)建掃描相應(yīng)符合條件的Bean掃描類(lèi)
- private ClassPathScanningCandidateComponentProvider createComponentProvider() {
 - ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false);
 - componentProvider.setEnvironment(this.applicationContext.getEnvironment());
 - componentProvider.setResourceLoader(this.applicationContext);
 - for (ServletComponentHandler handler : HANDLERS) {
 - componentProvider.addIncludeFilter(handler.getTypeFilter());
 - }
 - return componentProvider;
 - }
 
在該方法中為當(dāng)前的
ClassPathScanningCandidateComponentProvider類(lèi)掃描設(shè)置過(guò)濾器;過(guò)濾器在上面的static靜態(tài)代碼塊中已經(jīng)設(shè)置了WebServletHandler,WebFilterHandler,WebListenerHandler在父類(lèi)中分別創(chuàng)建不同注解的new AnnotationTypeFilter(annotationType)過(guò)濾類(lèi)。
創(chuàng)建完類(lèi)掃描類(lèi)以后開(kāi)始掃描通過(guò)該類(lèi)掃描相應(yīng)包(路徑)下的所有類(lèi)文件 檢查是否有對(duì)應(yīng)的注解。
3.4 查找及注冊(cè)Bean
- class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {
 - private void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, String packageToScan) {
 - for (BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)) {
 - if (candidate instanceof AnnotatedBeanDefinition) {
 - for (ServletComponentHandler handler : HANDLERS) {
 - handler.handle(((AnnotatedBeanDefinition) candidate), (BeanDefinitionRegistry) this.applicationContext);
 - }
 - }
 - }
 - }
 - }
 
findCandidateComponents方法查找候選的(符合條件)的類(lèi)并實(shí)例化為BeanDefinition對(duì)象。
方法執(zhí)行鏈findCandidateComponents ---》scanCandidateComponents
在scanCandidateComponents方法中查找符合條件的類(lèi),然后實(shí)例化為BeanDefinition
- public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
 - private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
 - Set<BeanDefinition> candidates = new LinkedHashSet<>();
 - MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
 - if (isCandidateComponent(metadataReader)) {
 - ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
 - sbd.setSource(resource);
 - if (isCandidateComponent(sbd)) {
 - candidates.add(sbd);
 - }
 - }
 - return candidates;
 - }
 - protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
 - for (TypeFilter tf : this.excludeFilters) {
 - if (tf.match(metadataReader, getMetadataReaderFactory())) {
 - return false;
 - }
 - }
 - for (TypeFilter tf : this.includeFilters) {
 - if (tf.match(metadataReader, getMetadataReaderFactory())) {
 - return isConditionMatch(metadataReader);
 - }
 - }
 - return false;
 - }
 - }
 
在第二個(gè)for開(kāi)始匹配所有的類(lèi)是否有相關(guān)的注解。如果匹配上就相應(yīng)的創(chuàng)建BeanDefinition對(duì)象放入集合Set中。
查找到所有的類(lèi)以后分別調(diào)用相應(yīng)的Web*Handler(ServletComponentHandler)進(jìn)行注冊(cè)Bean。
返回到上面的scanPackage方法中執(zhí)行handler.handle方法。
- abstract class ServletComponentHandler {
 - void handle(AnnotatedBeanDefinition beanDefinition, BeanDefinitionRegistry registry) {
 - Map<String, Object> attributes = beanDefinition.getMetadata().getAnnotationAttributes(this.annotationType.getName());
 - if (attributes != null) {
 - doHandle(attributes, beanDefinition, registry);
 - }
 - }
 - }
 
doHandle方法分別在子類(lèi)(WebServletHandler,WebFilterHandler,WebListenerHandler)中實(shí)現(xiàn),如上面已經(jīng)提到的WebServletHandler類(lèi)的doHandler方法。
到這里我們知道了注解的方式最終也是被注冊(cè)為ServletRegistrationBean 實(shí)例。那這個(gè)ServletRegistrationBean又是如何被容器(Tomcat)所感知的呢?
3.5 Tomcat注冊(cè)Servlet
在Web容器下 BeanFactory使用的是
AnnotationConfigServletWebServerApplicationContext
Spring IOC容器核心方法是refresh方法
- public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
 - public void refresh() throws BeansException, IllegalStateException {
 - synchronized (this.startupShutdownMonitor) {
 - try {
 - // Initialize other special beans in specific context subclasses.
 - onRefresh();
 - } catch (BeansException ex) {
 - throw ex;
 - } finally {
 - resetCommonCaches();
 - }
 - }
 - }
 - }
 
這里只留了onRefresh方法,進(jìn)入該方法:
onRefresh會(huì)進(jìn)入到子類(lèi)的方法
- public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
 - protected void onRefresh() {
 - super.onRefresh();
 - try {
 - createWebServer();
 - } catch (Throwable ex) {
 - throw new ApplicationContextException("Unable to start web server", ex);
 - }
 - }
 - }
 
進(jìn)入createWebServer方法
- public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
 - private void createWebServer() {
 - WebServer webServer = this.webServer;
 - ServletContext servletContext = getServletContext();
 - if (webServer == null && servletContext == null) {
 - ServletWebServerFactory factory = getWebServerFactory();
 - this.webServer = factory.getWebServer(getSelfInitializer());
 - getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));
 - getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
 - } else if (servletContext != null) {
 - try {
 - getSelfInitializer().onStartup(servletContext);
 - } catch (ServletException ex) {
 - throw new ApplicationContextException("Cannot initialize servlet context", ex);
 - }
 - }
 - initPropertySources();
 - }
 - }
 
這里會(huì)進(jìn)入factory.getWebServer(getSelfInitializer())方法執(zhí)行
進(jìn)入getSelfInitializer()方法
- public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
 - private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
 - return this::selfInitialize;
 - }
 - private void selfInitialize(ServletContext servletContext) throws ServletException {
 - prepareWebApplicationContext(servletContext);
 - registerApplicationScope(servletContext);
 - WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
 - for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
 - beans.onStartup(servletContext);
 - }
 - }
 - }
 
這里的for循環(huán)是遍歷當(dāng)前容器中所有ServletContextInitializer類(lèi)型的Bean。ServletRegistrationBean就是繼承ServletContextInitializer
到這里分別調(diào)用ServletContextInitializer的onStartup方法,進(jìn)入onStartup方法:
- public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
 - @Override
 - public final void onStartup(ServletContext servletContext) throws ServletException {
 - String description = getDescription();
 - if (!isEnabled()) {
 - logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
 - return;
 - }
 - register(description, servletContext);
 - }
 - // 在子類(lèi)中實(shí)現(xiàn)
 - protected abstract void register(String description, ServletContext servletContext);
 - }
 
進(jìn)入子類(lèi)DynamicRegistrationBean
- public abstract class DynamicRegistrationBean<D extends Registration.Dynamic> extends RegistrationBean {
 - @Override
 - protected final void register(String description, ServletContext servletContext) {
 - D registration = addRegistration(description, servletContext);
 - if (registration == null) {
 - logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
 - return;
 - }
 - // 配置Servlet Mapping相關(guān)的信息(在子類(lèi)ServletRegistrationBean中實(shí)現(xiàn)的)
 - configure(registration);
 - }
 - // 子類(lèi)中實(shí)現(xiàn)
 - protected abstract D addRegistration(String description, ServletContext servletContext);
 - }
 
進(jìn)入子類(lèi)ServletRegistrationBean
- public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> {
 - @Override
 - protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
 - String name = getServletName();
 - return servletContext.addServlet(name, this.servlet);
 - }
 - }
 
到這里就是通過(guò)ServletContext來(lái)動(dòng)態(tài)注冊(cè)Servlet(Servilet3.0)。
這里返回了
ServletRegistration.Dynamic對(duì)象會(huì)繼續(xù)執(zhí)行configure方法配置urlMapping等信息。
- public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> {
 - @Override
 - protected void configure(ServletRegistration.Dynamic registration) {
 - super.configure(registration);
 - String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
 - if (urlMapping.length == 0 && this.alwaysMapUrl) {
 - urlMapping = DEFAULT_MAPPINGS;
 - }
 - if (!ObjectUtils.isEmpty(urlMapping)) {
 - registration.addMapping(urlMapping);
 - }
 - registration.setLoadOnStartup(this.loadOnStartup);
 - if (this.multipartConfig != null) {
 - registration.setMultipartConfig(this.multipartConfig);
 - }
 - }
 - }
 
到此在Springboot環(huán)境下Servlet如何被注冊(cè)到Servlet容器中就已經(jīng)清晰了。這動(dòng)態(tài)注冊(cè)Servlet的相關(guān)API都是在Servlet3.0規(guī)范中才有的。















 
 
 
















 
 
 
 