Springboot注册Servlet几种方式你都知道?内部实现原理解析

环境:springboot2.3.9.RELEASE

1 Servlet注册

方式1:

在配置类(启动类)上添加@ServletComponentScan注解

@SpringBootApplication @ServletComponentScan public class SpringBootComprehensiveApplication } 

Servlet类上添加@WebServlet注解接口

@WebServlet("/hello") public class MyServlet extends HttpServlet {  }     

对应的注册t种Filter, Linstener有:@WebFilter,都知道内 @WebListener

方式2:

通过向IOC容器添加ServletRegistrationBean方式;该种方式可以在Servlet中注入其它Bean或者读取application.properties配置信息等。对应的部实filter, Listener有对应的bean;FilterRegistrationBean,ServletListenerRegistrationBean

@Bean public ServletRegistrationBean<MyServlet> servlet() {    ServletRegistrationBean<MyServlet> servlet = new ServletRegistrationBean<>(new MyServlet()) ;   servlet.addUrlMappings("/hello") ;    return servlet ; } 

 方式3:

动态注册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开始才有的功能,通过SPI技术在容器启动的现原析时候做一些初始化工作,比如注册Servlet等。理解在Servle规范中通过

ServletContainerInitializer实现该功能。注册t种

该Servlet规范的都知道内开发流程如下;

2、配置

ServletContainerInitializer

在src/META-INF/services下新建

javax.servlet.ServletContainerInitializer文件,部实文件内容为ServletContainerInitializer接口的现原析实现类(完整的包名+类名)如下:

com.pack.container.config.CustomServletInitializer 

CustomServletInitializer类

@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该注解会把属性value配置的值(ScoreWebInt.class)对应的所有类都收集上然后在onStartup方法中的Set 集合中应用。

在spring-web-xxxx.jar下就有通过该技术实现的理解相应功能。

3 扫描Servlet实现原理

在方式1中的注册t种实现原理就是扫描类路径下所有@WebServlet,@WebFilter,都知道内@WebListener。部实找到所有的现原析类后再通过方式2的方式进行注册。下面将核心源码贴出

3.1 导入核心类

// 该注解如果没有配置basePackages或者basePackageClasses属性,理解那么会读取当前添加@ServletComponentScan注解的b2b供应网类的包路径进行扫描。 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ServletComponentScanRegistrar.class) public @interface ServletComponentScan {  } // 注册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 {        // 当前容器中没有对应的Bean时执行该方法         addPostProcessor(registry, packagesToScan);     }   } } 

3.2 注册BeanFactory 处理器

ServletComponentScanRegistrar最核心的功能就是注册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 实例化扫描组件

进入

ServletComponentRegisteringPostProcessor类中首先这个类有个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);   } }     

这段代码分别是添加相应Servlet, Filter, Listener的处理句柄,分别处理@WebServlet,@WebFilter,@WebListener 注解。

查看WebServletHandler

class WebServletHandler extends ServletComponentHandler {    WebServletHandler() {      super(WebServlet.class);   }   // 看到该方法也就十分清楚了最终找到所有的Class以后,通过ServletRegistrationBean进行注册为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 }     // 父类ServletComponentHandler;在父类总添加相应的过滤器(分别查找相应注解的类,@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;   } } 

 接下来执行BeanFactoryPostProcessor对应的回调方法了

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方法进行创建扫描相应符合条件的Bean扫描类

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; } 

在该方法中为当前的

ClassPathScanningCandidateComponentProvider类扫描设置过滤器;过滤器在上面的static静态代码块中已经设置了WebServletHandler,WebFilterHandler,WebListenerHandler在父类中分别创建不同注解的new AnnotationTypeFilter(annotationType)过滤类。

创建完类扫描类以后开始扫描通过该类扫描相应包(路径)下的所有类文件 检查是否有对应的注解。

3.4 查找及注册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方法查找候选的(符合条件)的类并实例化为BeanDefinition对象。

方法执行链findCandidateComponents ---》scanCandidateComponents

在scanCandidateComponents方法中查找符合条件的类,站群服务器然后实例化为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;   } } 

在第二个for开始匹配所有的类是否有相关的注解。如果匹配上就相应的创建BeanDefinition对象放入集合Set中。

查找到所有的类以后分别调用相应的Web*Handler(ServletComponentHandler)进行注册Bean。

返回到上面的scanPackage方法中执行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方法分别在子类(WebServletHandler,WebFilterHandler,WebListenerHandler)中实现,如上面已经提到的WebServletHandler类的doHandler方法。

到这里我们知道了注解的方式最终也是被注册为ServletRegistrationBean 实例。那这个ServletRegistrationBean又是如何被容器(Tomcat)所感知的呢?

3.5 Tomcat注册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方法,进入该方法:

onRefresh会进入到子类的方法

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);     }   } }     

 进入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();   } }  

这里会进入factory.getWebServer(getSelfInitializer())方法执行

进入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循环是遍历当前容器中所有ServletContextInitializer类型的Bean。ServletRegistrationBean就是继承ServletContextInitializer

到这里分别调用ServletContextInitializer的免费信息发布网onStartup方法,进入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);   }   // 在子类中实现     protected abstract void register(String description, ServletContext servletContext);   } 

进入子类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相关的信息(在子类ServletRegistrationBean中实现的)       configure(registration);   }   // 子类中实现   protected abstract D addRegistration(String description, ServletContext servletContext); } 

 进入子类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);   } } 

 到这里就是通过ServletContext来动态注册Servlet(Servilet3.0)。

这里返回了

ServletRegistration.Dynamic对象会继续执行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环境下Servlet如何被注册到Servlet容器中就已经清晰了。这动态注册Servlet的相关API都是在Servlet3.0规范中才有的。

滇ICP备2023000592号-31